@@ -31,6 +31,34 @@ class AnnotationGenerator
3131{
3232 public const EXAMPLE_CHAR_LIMIT = 3000 ;
3333
34+ public const GLOBAL_PARAMETER_NAMES = [
35+ 'idSite ' ,
36+ 'period ' ,
37+ 'date ' ,
38+ 'segment ' ,
39+ 'expanded ' ,
40+ 'idSubtable ' ,
41+ 'flat ' ,
42+ 'filter_pattern ' ,
43+ 'filter_column ' ,
44+ 'filter_pattern_recursive ' ,
45+ 'filter_column_recursive ' ,
46+ 'filter_excludelowpop ' ,
47+ 'filter_excludelowpop_value ' ,
48+ 'filter_sort_column ' ,
49+ 'filter_sort_order ' ,
50+ 'filter_truncate ' ,
51+ 'filter_limit ' ,
52+ 'filter_offset ' ,
53+ 'keep_summary_row ' ,
54+ 'disable_generic_filters ' ,
55+ 'disable_queued_filters ' ,
56+ 'hideColumns ' ,
57+ 'showColumns ' ,
58+ 'label ' ,
59+ 'idGoal ' ,
60+ ];
61+
3462 /**
3563 * @var DocumentationGenerator
3664 */
@@ -41,9 +69,15 @@ class AnnotationGenerator
4169 */
4270 protected $ reportMetadata ;
4371
72+ /**
73+ * @var array[]
74+ */
75+ protected $ missingImportantDataWarnings ;
76+
4477 public function __construct (DocumentationGenerator $ generator )
4578 {
4679 $ this ->generator = $ generator ;
80+ $ this ->missingImportantDataWarnings = [];
4781 }
4882
4983 /**
@@ -65,7 +99,7 @@ public function __construct(DocumentationGenerator $generator)
6599 */
66100 public function generatePluginApiAnnotations (string $ pluginName , bool $ writeToFile = false , bool $ useTmpDir = false ): array
67101 {
68- BaseValidator::check ('plugin ' , $ pluginName , [ new NotEmpty () ]);
102+ BaseValidator::check ('plugin ' , $ pluginName , [new NotEmpty ()]);
69103 Manager::getInstance ()->checkIsPluginActivated ($ pluginName );
70104
71105 $ currentPluginDir = Manager::getInstance ()::getPluginDirectory ('OpenApiDocs ' );
@@ -107,7 +141,28 @@ public function generatePluginApiAnnotations(string $pluginName, bool $writeToFi
107141 $ this ->writeAnnotationsToFile ($ annotations , $ pluginAnnotationPath , $ pluginName );
108142 }
109143
110- return $ annotations ;
144+ if (count ($ this ->missingImportantDataWarnings ) === 0 ) {
145+ return $ annotations ;
146+ }
147+
148+ $ lines = [];
149+ foreach ($ this ->missingImportantDataWarnings as $ methodName => $ warnings ) {
150+ if (empty ($ warnings )) {
151+ continue ;
152+ }
153+
154+ $ lines [] = $ methodName . ' has the following warnings: ' ;
155+ foreach ($ warnings as $ paramName => $ warningLines ) {
156+ if (empty ($ warningLines )) {
157+ continue ;
158+ }
159+
160+ $ lines [] = '- ' . $ paramName . ': ' ;
161+ $ lines [] = " - " . implode ("\n - " , $ warningLines );
162+ }
163+ }
164+
165+ return $ lines ;
111166 }
112167
113168 /**
@@ -215,18 +270,18 @@ protected function buildAnnotationForMethod(array $rules, string $pluginName, \R
215270 */
216271 public function getParamInfoFromDocBlock (string $ docBlock ): array
217272 {
218- $ lexer = new Lexer ();
273+ $ lexer = new Lexer ();
219274 $ tokens = $ lexer ->tokenize ($ docBlock );
220275 $ expressionParser = new ConstExprParser ();
221276 $ parser = new PhpDocParser (new TypeParser ($ expressionParser ), $ expressionParser );
222- $ node = $ parser ->parse (new TokenIterator ($ tokens ));
277+ $ node = $ parser ->parse (new TokenIterator ($ tokens ));
223278
224279 $ params = [];
225280 foreach ($ node ->getParamTagValues () as $ param ) {
226281 $ name = ltrim ($ param ->parameterName , '$ ' );
227282 $ params [$ name ] = [
228- 'type ' => (string ) $ param ->type ,
229- // Normalise the description. E.g. remove linebreaks and indentation
283+ 'type ' => (string )$ param ->type ,
284+ // Normalise the description. E.g. remove linebreaks and indentation
230285 'description ' => trim (preg_replace (['/^\h+/m ' , '/\R+/u ' ,], ['' , ' ' ], $ param ->description )),
231286 'byRef ' => $ param ->isReference ,
232287 'variadic ' => $ param ->isVariadic ,
@@ -246,11 +301,11 @@ public function getParamInfoFromDocBlock(string $docBlock): array
246301 */
247302 public function getResponseInfoFromDocBlock (string $ docBlock ): array
248303 {
249- $ lexer = new Lexer ();
304+ $ lexer = new Lexer ();
250305 $ tokens = $ lexer ->tokenize ($ docBlock );
251306 $ expressionParser = new ConstExprParser ();
252307 $ parser = new PhpDocParser (new TypeParser ($ expressionParser ), $ expressionParser );
253- $ node = $ parser ->parse (new TokenIterator ($ tokens ));
308+ $ node = $ parser ->parse (new TokenIterator ($ tokens ));
254309
255310 $ responseInfo = ['type ' => null ];
256311 $ returnTags = $ node ->getReturnTagValues ();
@@ -293,6 +348,7 @@ public function buildVirtualPath(string $virtualPathTemplate, string $plugin, st
293348 * Build the key data for the specified parameter. This should be all the data necessary to create an OA\Parameter
294349 * annotation object.
295350 *
351+ * @param string $methodName The name of the method. E.g. getAlert
296352 * @param string $paramName The name of the parameter. E.g. idSite or period
297353 * @param array $paramMetadata The collection of metadata from the old DocumentationGenerator class. Things like
298354 * whether the parameter is typed, is required, or has a default value.
@@ -312,9 +368,12 @@ public function buildVirtualPath(string $virtualPathTemplate, string $plugin, st
312368 * 'example' => 1,
313369 * ]
314370 */
315- public function buildParameterAnnotationData (string $ paramName , array $ paramMetadata , array $ paramDocInfo ): array
371+ public function buildParameterAnnotationData (string $ methodName , string $ paramName , array $ paramMetadata , array $ paramDocInfo ): array
316372 {
317373 $ docType = strtolower (trim ($ paramDocInfo ['type ' ] ?? '' ));
374+ if (empty ($ docType )) {
375+ $ this ->addMissingImportantDataWarning ($ methodName , $ paramName , 'Type is not specified in comment block. ' );
376+ }
318377 $ metaType = strtolower (trim ($ paramMetadata ['type ' ] ?? $ docType ));
319378 $ type = $ metaType === 'string ' && $ docType !== 'string ' ? $ docType : $ metaType ;
320379 // If the signature type is array, but the type hinting provides more, use that instead
@@ -336,6 +395,9 @@ public function buildParameterAnnotationData(string $paramName, array $paramMeta
336395
337396 $ isRequired = !key_exists ('default ' , $ paramMetadata ) || $ paramMetadata ['default ' ] instanceof NoDefaultValue;
338397 $ description = $ paramDocInfo ['description ' ] ?? '' ;
398+ if (empty ($ description )) {
399+ $ this ->addMissingImportantDataWarning ($ methodName , $ paramName , 'Description is not specified in comment block. ' );
400+ }
339401 $ example = '' ;
340402 // Check the description for the example value
341403 if (preg_match ('/\[@example\s*=\s*([^\n]+)\]/ ' , $ description , $ m )) {
@@ -349,6 +411,10 @@ public function buildParameterAnnotationData(string $paramName, array $paramMeta
349411 $ example = trim ($ example , '" ' );
350412 }
351413
414+ // Clean up the descriptions a little more like removing linebreaks and escaping double-quotes
415+ $ description = str_replace ("\n" , ' ' , $ description );
416+ $ description = str_replace ('" ' , '"" ' , $ description );
417+
352418 return [
353419 'name ' => $ paramName ,
354420 'types ' => $ typesMap ,
@@ -359,6 +425,48 @@ public function buildParameterAnnotationData(string $paramName, array $paramMeta
359425 ];
360426 }
361427
428+ /**
429+ * Add an entry to the map of warnings about missing important information, like type and description of parameters
430+ * and returns.
431+ *
432+ * @param string $methodName Name of the method to more easily identify where in the code needs adjustment.
433+ * @param string $paramName Name of the parameter or "return" for the response. E.g. idSite, period, return, ...
434+ * @param string $message Message indicating what is missing. E.g. "Type is not specified in comment block."
435+ *
436+ * @return void
437+ */
438+ protected function addMissingImportantDataWarning (string $ methodName , string $ paramName , string $ message ): void
439+ {
440+ // Make sure that the inner arrays have been initialised and then add the message to the warning map
441+ $ this ->missingImportantDataWarnings [$ methodName ] = $ this ->missingImportantDataWarnings [$ methodName ] ?? [];
442+ $ this ->missingImportantDataWarnings [$ methodName ][$ paramName ] = $ this ->missingImportantDataWarnings [$ methodName ][$ paramName ] ?? [];
443+ $ this ->missingImportantDataWarnings [$ methodName ][$ paramName ][] = $ message ;
444+ }
445+
446+ /**
447+ * Remove a warning from the collection. This is useful when it's determined after the fact that a parameter has
448+ * a global component which can be used, like idSite or period.
449+ *
450+ * @param string $methodName Name of the method.
451+ * @param string $paramName Name of the parameter or "return" for the response. E.g. idSite, period, return, ...
452+ *
453+ * @return void
454+ */
455+ protected function removeMissingImportantDataWarning (string $ methodName , string $ paramName ): void
456+ {
457+ if (empty ($ this ->missingImportantDataWarnings [$ methodName ][$ paramName ])) {
458+ return ;
459+ }
460+
461+ // If it's the only param in the collection for the method, remove the method
462+ if (count ($ this ->missingImportantDataWarnings [$ methodName ]) === 1 ) {
463+ unset($ this ->missingImportantDataWarnings [$ methodName ]);
464+ return ;
465+ }
466+
467+ unset($ this ->missingImportantDataWarnings [$ methodName ][$ paramName ]);
468+ }
469+
362470 /**
363471 * Build the collection of parameters and key information about them for the specified method.
364472 *
@@ -399,7 +507,16 @@ protected function determineParameters(array $rules, string $plugin, string $met
399507 continue ;
400508 }
401509
402- $ customParams [] = $ this ->buildParameterAnnotationData ($ name , $ paramMetadata , $ paramInfo );
510+ // If the parameter doesn't have a description and matches a global, use a reference to the global instead.
511+ $ customParamData = $ this ->buildParameterAnnotationData ($ method , $ name , $ paramMetadata , $ paramInfo );
512+ if (empty ($ customParamData ['description ' ]) && in_array ($ name , self ::GLOBAL_PARAMETER_NAMES )) {
513+ $ globalParamSuffix = $ customParamData ['required ' ] === 'true ' ? 'Required ' : 'Optional ' ;
514+ $ refs [] = '#/components/parameters/ ' . $ name . $ globalParamSuffix ;
515+ $ this ->removeMissingImportantDataWarning ($ method , $ name );
516+ continue ;
517+ }
518+
519+ $ customParams [] = $ customParamData ;
403520 }
404521
405522 return [
@@ -415,6 +532,7 @@ protected function determineParameters(array $rules, string $plugin, string $met
415532 * @link https://spec.openapis.org/oas/v3.1.1.html#data-types
416533 *
417534 * @param string $type The PHP type from the method signature or doc-block
535+ *
418536 * @return string The normalised Data Type to be used in the swagger-php annotation
419537 */
420538 public function getOpenApiTypeFromPhpType (string $ type ): string
@@ -812,6 +930,8 @@ protected function determineResponses(array $rules, string $plugin, string $meth
812930
813931 if (!empty ($ responseInfo ['description ' ])) {
814932 $ successArray ['description ' ] = $ responseInfo ['description ' ];
933+ } elseif (empty ($ successArray ['ref ' ])) {
934+ $ this ->addMissingImportantDataWarning ($ method , 'return ' , 'Description is not specified in comment block. ' );
815935 }
816936
817937 $ responseSchema = !empty ($ responseInfo ['type ' ]) ? $ this ->buildSchemaObjectArray ($ responseInfo ['type ' ]) : [];
@@ -883,7 +1003,7 @@ protected function determineResponses(array $rules, string $plugin, string $meth
8831003 $ successArray ['description ' ] = '' ;
8841004 }
8851005 } else {
886- // Make sure the schema is included in there are no examples
1006+ // Make sure the schema is included if there are no examples
8871007 $ successArray ['schema ' ] = $ responseSchema ;
8881008 }
8891009
@@ -897,6 +1017,10 @@ protected function determineResponses(array $rules, string $plugin, string $meth
8971017 // Append the links to the description with a prefix linebreak. If there's no description, skip the break
8981018 $ successArray ['description ' ] .= (!empty ($ successArray ['description ' ]) && !empty ($ descriptionLinks ) ? '</br> ' : '' ) . $ descriptionLinks ;
8991019
1020+ if (empty ($ successArray ['ref ' ]) && empty ($ descriptionLinks ) && empty ($ successArray ['schema ' ])) {
1021+ $ this ->addMissingImportantDataWarning ($ method , 'return ' , 'Type could not be determined via comment block or example. ' );
1022+ }
1023+
9001024 $ responses [] = $ successArray ;
9011025
9021026 if (!empty ($ rules ['defaultErrorResponseRefs ' ])) {
0 commit comments