|
3 | 3 | namespace OpenAPIExtractor; |
4 | 4 |
|
5 | 5 |
|
| 6 | +use Exception; |
6 | 7 | use PhpParser\Node\Stmt\ClassMethod; |
7 | 8 | use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; |
8 | 9 | use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; |
@@ -61,7 +62,11 @@ static function parse(string $context, array $definitions, ClassMethod $method, |
61 | 62 | foreach ($docNodes as $docNode) { |
62 | 63 | if ($docNode instanceof PhpDocTagNode) { |
63 | 64 | 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 | + } |
65 | 70 | } |
66 | 71 |
|
67 | 72 | if ($docNode->value instanceof ReturnTagValueNode) { |
@@ -95,25 +100,77 @@ static function parse(string $context, array $definitions, ClassMethod $method, |
95 | 100 | } |
96 | 101 |
|
97 | 102 | foreach ($methodParameters as $methodParameter) { |
98 | | - $param = null; |
99 | 103 | $methodParameterName = $methodParameter->var->name; |
100 | 104 |
|
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 | + } |
107 | 120 | } |
108 | 121 | } |
109 | 122 |
|
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; |
113 | 130 | } else { |
114 | | - Logger::error($context, "Missing doc parameter for '" . $methodParameterName . "'"); |
115 | | - continue; |
| 131 | + $description = ""; |
116 | 132 | } |
| 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; |
117 | 174 | } |
118 | 175 |
|
119 | 176 | if (!$allowMissingDocs && $param->type->description == "") { |
|
0 commit comments