@@ -437,15 +437,25 @@ public function buildParameterAnnotationData(string $methodName, string $paramNa
437437 if (count ($ typeHints ) > 1 && in_array ('bool ' , $ typeHints )) {
438438 $ typeHints = array_diff ($ typeHints , ['bool ' ]);
439439 }
440- foreach ($ typeHints as $ typePart ) {
441- $ typePart = trim ($ typePart , ' () ' );
442- $ normalisedType = $ this ->getOpenApiTypeFromPhpType ($ typePart );
443- // If the type is array, check if there's a subType
444- $ subType = null ;
445- if ($ normalisedType === 'array ' && $ typePart !== 'array ' && strpos ($ typePart , '[] ' ) !== false ) {
446- $ subType = substr ($ typePart , 0 , strpos ($ typePart , '[] ' ));
440+
441+ $ allTypeHintsAreStringLiterals = $ this ->areAllTypeHintsStringLiterals ($ typeHints );
442+ $ enumValues = [];
443+ if ($ allTypeHintsAreStringLiterals ) {
444+ $ typesMap ['string ' ] = null ;
445+ foreach ($ typeHints as $ typeHint ) {
446+ $ enumValues [] = trim (trim ($ typeHint ), '\'" ' );
447+ }
448+ } else {
449+ foreach ($ typeHints as $ typePart ) {
450+ $ typePart = trim ($ typePart , ' () ' );
451+ $ normalisedType = $ this ->getOpenApiTypeFromPhpType ($ typePart );
452+ // If the type is array, check if there's a subType
453+ $ subType = null ;
454+ if ($ normalisedType === 'array ' && $ typePart !== 'array ' && strpos ($ typePart , '[] ' ) !== false ) {
455+ $ subType = substr ($ typePart , 0 , strpos ($ typePart , '[] ' ));
456+ }
457+ $ typesMap [$ normalisedType ] = $ subType !== null ? $ this ->getOpenApiTypeFromPhpType ($ subType ) : $ subType ;
447458 }
448- $ typesMap [$ normalisedType ] = $ subType !== null ? $ this ->getOpenApiTypeFromPhpType ($ subType ) : $ subType ;
449459 }
450460
451461 $ isRequired = !key_exists ('default ' , $ paramMetadata ) || $ paramMetadata ['default ' ] instanceof NoDefaultValue;
@@ -474,14 +484,45 @@ public function buildParameterAnnotationData(string $methodName, string $paramNa
474484 $ default = json_encode ($ default );
475485 }
476486
477- return [
487+ $ paramData = [
478488 'name ' => $ paramName ,
479489 'types ' => $ typesMap ,
480490 'description ' => $ description ,
481491 'required ' => $ isRequired ? 'true ' : 'false ' ,
482492 'default ' => !$ isRequired ? $ default : NoDefaultValue::class,
483493 'example ' => $ example ,
484494 ];
495+
496+ if (!empty ($ enumValues )) {
497+ $ paramData ['enum ' ] = $ enumValues ;
498+ }
499+
500+ return $ paramData ;
501+ }
502+
503+ /**
504+ * Determine whether all type hints are quoted string literals.
505+ *
506+ * @param array $typeHints
507+ *
508+ * @return bool
509+ */
510+ protected function areAllTypeHintsStringLiterals (array $ typeHints ): bool
511+ {
512+ if (empty ($ typeHints )) {
513+ return false ;
514+ }
515+
516+ foreach ($ typeHints as $ typeHint ) {
517+ $ typeHint = trim (strval ($ typeHint ));
518+ $ firstChar = $ typeHint [0 ] ?? '' ;
519+ $ lastChar = $ typeHint [strlen ($ typeHint ) - 1 ] ?? '' ;
520+ if (!(($ firstChar === "' " && $ lastChar === "' " ) || ($ firstChar === '" ' && $ lastChar === '" ' ))) {
521+ return false ;
522+ }
523+ }
524+
525+ return true ;
485526 }
486527
487528 /**
@@ -1715,12 +1756,19 @@ public function buildLinesForAnnotationObject(string $objectName, array $objectP
17151756 * @param string $subType This can specify the subtype for arrays. E.g. integer for int[] or string for string[].
17161757 * @param string $default The optional default value for the type. Default is no value.
17171758 * @param string $example The optional example value for the type. Default is empty string which indicated no value.
1759+ * @param array $enum The optional enum values for the type.
17181760 *
17191761 * @return array[]
17201762 */
1721- public function buildSchemaObjectArray (string $ type , string $ subType = '' , string $ default = NoDefaultValue::class, string $ example = '' ): array
1763+ public function buildSchemaObjectArray (string $ type , string $ subType = '' , string $ default = NoDefaultValue::class, string $ example = '' , array $ enum = [] ): array
17221764 {
17231765 $ schemaMap = ['type=" ' . $ type . '" ' ];
1766+ if (!empty ($ enum ) && $ type === 'string ' ) {
1767+ $ enumStringValues = array_map (static function ($ value ) {
1768+ return '" ' . str_replace ('" ' , '\" ' , strval ($ value )) . '" ' ;
1769+ }, $ enum );
1770+ $ schemaMap [] = 'enum={ ' . implode (', ' , $ enumStringValues ) . '} ' ;
1771+ }
17241772 if (($ example ) !== '' ) {
17251773 $ schemaMap [] = 'example= ' . $ this ->wrapStringWithQuotes ($ example , $ type );
17261774 }
@@ -1800,14 +1848,16 @@ public function shouldIncludeDefault(string $type, string $default = NoDefaultVa
18001848 * default is set.
18011849 * @param string $example The value to use as the example property of the schema. If it's an empty string, no
18021850 * example is set.
1851+ * @param array $enum The optional enum values to apply to string schemas.
18031852 *
18041853 * @return array[] The collection of lines which make up the schema annotation object.
18051854 */
1806- public function buildSchemaObjectArrays (array $ typesMap , string $ default = '' , string $ example = '' ): array
1855+ public function buildSchemaObjectArrays (array $ typesMap , string $ default = '' , string $ example = '' , array $ enum = [] ): array
18071856 {
18081857 $ schemas = [];
18091858 foreach ($ typesMap as $ type => $ subType ) {
1810- $ schemas [] = $ this ->buildSchemaObjectArray ($ type , $ subType ?? '' , $ default , $ example );
1859+ $ schemaEnum = $ type === 'string ' ? $ enum : [];
1860+ $ schemas [] = $ this ->buildSchemaObjectArray ($ type , $ subType ?? '' , $ default , $ example , $ schemaEnum );
18111861 }
18121862
18131863 if (count ($ schemas ) === 1 ) {
@@ -1867,7 +1917,12 @@ public function compileOperationLines(string $path, string $opId, string $plugin
18671917 // Escape quotes differently for the annotation examples
18681918 $ exampleString = str_replace ('\" ' , '"" ' , $ exampleString );
18691919 }
1870- $ paramMap [] = $ this ->buildSchemaObjectArrays ($ param ['types ' ], strval ($ param ['default ' ]), strval ($ exampleString ));
1920+ $ paramMap [] = $ this ->buildSchemaObjectArrays (
1921+ $ param ['types ' ],
1922+ strval ($ param ['default ' ]),
1923+ strval ($ exampleString ),
1924+ $ param ['enum ' ] ?? []
1925+ );
18711926 $ operationValuesMap [] = ['@OA\Parameter ' => $ paramMap ];
18721927 }
18731928 foreach ($ responses as $ response ) {
0 commit comments