Skip to content

Commit 88fa50f

Browse files
committed
More cleanup and adding TODOs
1 parent 91f17fd commit 88fa50f

2 files changed

Lines changed: 95 additions & 12 deletions

File tree

Annotations/AnnotationGenerator.php

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
use Piwik\Plugin\Manager;
1717
use Piwik\Validators\BaseValidator;
1818
use Piwik\Validators\NotEmpty;
19+
use PHPStan\PhpDocParser\Lexer\Lexer;
20+
use PHPStan\PhpDocParser\Parser\PhpDocParser;
21+
use PHPStan\PhpDocParser\Parser\TypeParser;
22+
use PHPStan\PhpDocParser\Parser\ConstExprParser;
23+
use PHPStan\PhpDocParser\Parser\TokenIterator;
24+
use function Symfony\Component\String\s;
1925

2026
class AnnotationGenerator
2127
{
@@ -69,13 +75,13 @@ public function generatePluginApiAnnotations(string $pluginName)
6975
$methodName
7076
);
7177

72-
$paramRefs = $this->determineParameterReferences($rules, $pluginName, $methodName, $reflectionMethod);
78+
$params = $this->determineParameters($rules, $pluginName, $methodName, $reflectionMethod);
7379
$responses = $this->determineResponses($rules, $pluginName, $methodName);
7480

7581
$isPost = !empty($rules['plugins'][$pluginName]['methodsRequiringPost'])
7682
&& in_array($methodName, $rules['plugins'][$pluginName]['methodsRequiringPost']);
7783

78-
$annotations[] = $this->compileOperationLines($path, $opId, $pluginName, $methodName, $paramRefs, $responses, $isPost);
84+
$annotations[] = $this->compileOperationLines($path, $opId, $pluginName, $methodName, $params, $responses, $isPost);
7985
}
8086

8187
if (empty($annotations)) {
@@ -85,12 +91,32 @@ public function generatePluginApiAnnotations(string $pluginName)
8591
return $annotations;
8692
}
8793

94+
function getParamInfoFromDocBlock(string $docBlock): array {
95+
$lexer = new Lexer();
96+
$tokens = $lexer->tokenize($docBlock);
97+
$expressionParser = new ConstExprParser();
98+
$parser = new PhpDocParser(new TypeParser($expressionParser), $expressionParser);
99+
$node = $parser->parse(new TokenIterator($tokens));
100+
101+
$params = [];
102+
foreach ($node->getParamTagValues() as $param) {
103+
$name = ltrim($param->parameterName, '$');
104+
$params[$name] = [
105+
'type' => (string) $param->type,
106+
'desc' => $param->description,
107+
'byRef' => $param->isReference,
108+
'variadic' => $param->isVariadic,
109+
];
110+
}
111+
return $params;
112+
}
113+
88114
function buildVirtualPath(string $virtualPathTemplate, string $plugin, string $method): string
89115
{
90116
return str_replace([ '{plugin}', '{method}' ], [ $plugin, $method ], $virtualPathTemplate);
91117
}
92118

93-
function determineParameterReferences(array $rules, string $plugin, string $method, \ReflectionMethod $rm): array
119+
function determineParameters(array $rules, string $plugin, string $method, \ReflectionMethod $reflectionMethod): array
94120
{
95121
$refs = [];
96122

@@ -102,33 +128,73 @@ function determineParameterReferences(array $rules, string $plugin, string $meth
102128
$refs = array_merge($refs, $rules['plugins'][$plugin]['paramRefsByMethod'][$method]);
103129
}
104130

105-
return array_values(array_unique($refs));
131+
$paramsMetadata = Proxy::getInstance()->getParametersListWithTypes(Request::getClassNameAPI($plugin), $method);
132+
$paramsInfo = $this->getParamInfoFromDocBlock($reflectionMethod->getDocComment());
133+
134+
$customParams = [];
135+
foreach ($paramsMetadata as $name => $paramMetadata) {
136+
$paramInfo = $paramsInfo[$name] ?? [];
137+
// Skip references and variadic for now
138+
// TODO - determine whether these can be handled automatically or if they have to be manual
139+
if (!empty($paramInfo['byRef']) || !empty($paramInfo['variadic'])) {
140+
continue;
141+
}
142+
143+
$type = $paramMetadata['type'] ?? $paramInfo['type'] ?? '';
144+
// TODO - Properly map the internal types to OpenAPI types
145+
switch (strtolower($type)) {
146+
case 'array':
147+
$type = 'array';
148+
break;
149+
case 'int':
150+
$type = 'integer';
151+
break;
152+
case 'bool':
153+
case 'boolean':
154+
$type = 'boolean';
155+
break;
156+
default:
157+
$type = 'string';
158+
}
159+
160+
$customParams[] = [
161+
'name' => $name,
162+
'type' => $type,
163+
'description' => $paramInfo['desc'] ?? '',
164+
'required' => empty($paramMetadata['allowsNull']) ? 'true' : 'false',
165+
];
166+
}
167+
168+
return [
169+
'refs' => array_values(array_unique($refs)),
170+
'custom' => $customParams,
171+
];
106172
}
107173

108174
function determineResponses(array $rules, string $plugin, string $method): array
109175
{
110-
$out = [];
176+
$responses = [];
111177

112178
$successRef = null;
113179
if (isset($rules['plugins'][$plugin]['successResponseByMethod'][$method])) {
114180
$successRef = $rules['plugins'][$plugin]['successResponseByMethod'][$method];
115181
}
116182
if ($successRef) {
117-
$out[] = [ 'code' => 200, 'ref' => $successRef ];
183+
$responses[] = [ 'code' => 200, 'ref' => $successRef ];
118184
} else {
119-
$out[] = [ 'code' => 200, 'desc' => 'OK' ];
185+
$responses[] = [ 'code' => 200 ];
120186
}
121187

122188
if (!empty($rules['defaultErrorResponseRefs'])) {
123-
foreach ($rules['defaultErrorResponseRefs'] as $err) {
124-
$out[] = $err; // ['code'=>..., 'ref'=>...]
189+
foreach ($rules['defaultErrorResponseRefs'] as $errorRef) {
190+
$responses[] = $errorRef;
125191
}
126192
}
127193

128-
return $out;
194+
return $responses;
129195
}
130196

131-
function compileOperationLines(string $path, string $opId, string $plugin, string $method, array $paramRefs, array $responses, bool $isPost = false): array
197+
function compileOperationLines(string $path, string $opId, string $plugin, string $method, array $params, array $responses, bool $isPost = false): array
132198
{
133199
$httpMethod = $isPost ? 'Post' : 'Get';
134200
$lines = [];
@@ -137,10 +203,22 @@ function compileOperationLines(string $path, string $opId, string $plugin, strin
137203
$lines[] = ' operationId="' . $opId . '",';
138204
$lines[] = ' tags={"' . $plugin . '"},';
139205

140-
foreach ($paramRefs as $ref) {
206+
foreach ($params['refs'] ?? [] as $ref) {
141207
$lines[] = ' @OA\Parameter(ref="' . $ref . '"),';
142208
}
143209

210+
foreach ($params['custom'] ?? [] as $param) {
211+
// TODO - Finish implementing this
212+
$lines[] = ' @OA\Parameter(),';
213+
$lines[] = ' name="' . $param['name'] . '",';
214+
$lines[] = ' in="query",';
215+
$lines[] = ' required="' . $param['required'] . '",';
216+
$lines[] = ' @OA\Schema(';
217+
$lines[] = ' type="' . $param['type'] . '",';
218+
$lines[] = ' ),';
219+
$lines[] = ' ),';
220+
}
221+
144222
foreach ($responses as $response) {
145223
if (isset($response['ref'])) {
146224
$code = $response['code'];

Commands/GenerateAnnotations.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ protected function doExecute(): int
7171
$input = $this->getInput();
7272
$output = $this->getOutput();
7373

74+
if (!class_exists('PHPStan\PhpDocParser\Parser\PhpDocParser')) {
75+
$output->writeln("<error>This command requires phpstan/phpdoc-parser. It should be available while running Matomo in development mode.</error>");
76+
return self::FAILURE;
77+
}
78+
7479
$plugin = $input->getOption('plugin') ?: 'Matomo';
7580
$notDryRun = $input->getOption('not-dry-run') ?: false;
7681

0 commit comments

Comments
 (0)