Skip to content

Commit 12faea2

Browse files
committed
fix: Explicit @psalm-param handling
Signed-off-by: jld3103 <jld3103yt@gmail.com>
1 parent 80cfa10 commit 12faea2

3 files changed

Lines changed: 75 additions & 18 deletions

File tree

generate-spec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ foreach ($routes as $route) {
427427
}
428428

429429
$schema = $parameter->type->toArray($openapiVersion, true);
430-
$description = $parameter?->docParameter != null && $parameter->docParameter->description != "" ? Helpers::cleanDocComment($parameter->docParameter->description) : null;
430+
$description = $parameter?->docType != null && $parameter->docType->description != "" ? Helpers::cleanDocComment($parameter->docType->description) : null;
431431
} else {
432432
$schema = [
433433
"type" => "string",
@@ -585,7 +585,7 @@ foreach ($routes as $route) {
585585
"name" => $parameter->name . ($parameter->type->type == "array" ? "[]" : ""),
586586
"in" => "query",
587587
],
588-
$parameter->docParameter != null && $parameter->docParameter->description != "" ? ["description" => Helpers::cleanDocComment($parameter->docParameter->description)] : [],
588+
$parameter->docType != null && $parameter->docType->description != "" ? ["description" => Helpers::cleanDocComment($parameter->docType->description)] : [],
589589
!$parameter->type->nullable && !$parameter->type->hasDefaultValue ? ["required" => true] : [],
590590
["schema" => $parameter->type->toArray($openapiVersion, true),],
591591
), $queryParameters),

src/ControllerMethod.php

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace OpenAPIExtractor;
44

55

6+
use Exception;
67
use PhpParser\Node\Stmt\ClassMethod;
78
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
89
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
@@ -61,7 +62,11 @@ static function parse(string $context, array $definitions, ClassMethod $method,
6162
foreach ($docNodes as $docNode) {
6263
if ($docNode instanceof PhpDocTagNode) {
6364
if ($docNode->value instanceof ParamTagValueNode) {
64-
$docParameters[] = $docNode->value;
65+
if (array_key_exists($docNode->name, $docParameters)) {
66+
$docParameters[$docNode->name][] = $docNode->value;
67+
} else {
68+
$docParameters[$docNode->name] = [$docNode->value];
69+
}
6570
}
6671

6772
if ($docNode->value instanceof ReturnTagValueNode) {
@@ -95,25 +100,77 @@ static function parse(string $context, array $definitions, ClassMethod $method,
95100
}
96101

97102
foreach ($methodParameters as $methodParameter) {
98-
$param = null;
99103
$methodParameterName = $methodParameter->var->name;
100104

101-
foreach ($docParameters as $docParameter) {
102-
$docParameterName = substr($docParameter->parameterName, 1);
103-
104-
if ($docParameterName == $methodParameterName) {
105-
$param = new ControllerMethodParameter($context, $definitions, $methodParameterName, $methodParameter, $docParameter);
106-
break;
105+
$paramTag = null;
106+
$psalmParamTag = null;
107+
foreach ($docParameters as $docParameterType => $typeDocParameters) {
108+
foreach ($typeDocParameters as $docParameter) {
109+
$docParameterName = substr($docParameter->parameterName, 1);
110+
111+
if ($docParameterName == $methodParameterName) {
112+
if ($docParameterType == "@param") {
113+
$paramTag = $docParameter;
114+
} else if ($docParameterType == "@psalm-param") {
115+
$psalmParamTag = $docParameter;
116+
} else {
117+
Logger::panic($context, "Unknown param type " . $docParameterType);
118+
}
119+
}
107120
}
108121
}
109122

110-
if ($param == null) {
111-
if ($allowMissingDocs) {
112-
$param = new ControllerMethodParameter($context, $definitions, $methodParameterName, $methodParameter, null);
123+
if ($paramTag !== null && $psalmParamTag !== null) {
124+
// Use all the type information from @psalm-param because it is more specific,
125+
// but pull the description from @param and @psalm-param because usually only one of them has it.
126+
if ($psalmParamTag->description !== "") {
127+
$description = $psalmParamTag->description;
128+
} else if ($paramTag->description !== "") {
129+
$description = $paramTag->description;
113130
} else {
114-
Logger::error($context, "Missing doc parameter for '" . $methodParameterName . "'");
115-
continue;
131+
$description = "";
116132
}
133+
134+
try {
135+
$type = OpenApiType::resolve(
136+
$context,
137+
$definitions,
138+
new ParamTagValueNode(
139+
$psalmParamTag->type,
140+
$psalmParamTag->isVariadic,
141+
$psalmParamTag->parameterName,
142+
$description,
143+
$psalmParamTag->isReference,
144+
),
145+
);
146+
} catch (LoggerException $e) {
147+
Logger::warning($context, "Unable to parse parameter " . $methodParameterName . ": " . $e->message . "\n" . $e->getTraceAsString());
148+
// Fallback to the @param annotation
149+
$type = OpenApiType::resolve(
150+
$context,
151+
$definitions,
152+
new ParamTagValueNode(
153+
$paramTag->type,
154+
$paramTag->isVariadic,
155+
$paramTag->parameterName,
156+
$description,
157+
$paramTag->isReference,
158+
),
159+
);
160+
}
161+
162+
$param = new ControllerMethodParameter($context, $definitions, $methodParameterName, $methodParameter, $type);
163+
} else if ($psalmParamTag !== null) {
164+
$type = OpenApiType::resolve($context, $definitions, $psalmParamTag);
165+
$param = new ControllerMethodParameter($context, $definitions, $methodParameterName, $methodParameter, $type);
166+
} else if ($paramTag !== null) {
167+
$type = OpenApiType::resolve($context, $definitions, $paramTag);
168+
$param = new ControllerMethodParameter($context, $definitions, $methodParameterName, $methodParameter, $type);
169+
} else if ($allowMissingDocs) {
170+
$param = new ControllerMethodParameter($context, $definitions, $methodParameterName, $methodParameter, null);
171+
} else {
172+
Logger::error($context, "Missing doc parameter for '" . $methodParameterName . "'");
173+
continue;
117174
}
118175

119176
if (!$allowMissingDocs && $param->type->description == "") {

src/ControllerMethodParameter.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
class ControllerMethodParameter {
1717
public OpenApiType $type;
1818

19-
public function __construct(string $context, array $definitions, public string $name, public Param $methodParameter, public ?ParamTagValueNode $docParameter) {
20-
if ($docParameter != null) {
21-
$this->type = OpenApiType::resolve($context, $definitions, $docParameter);
19+
public function __construct(string $context, array $definitions, public string $name, public Param $methodParameter, public ?OpenApiType $docType) {
20+
if ($docType != null) {
21+
$this->type = $this->docType;
2222
} else {
2323
$this->type = OpenApiType::resolve($context, $definitions, $methodParameter->type);
2424
}

0 commit comments

Comments
 (0)