@@ -43,7 +43,7 @@ public function isArrayShape(): bool
4343 }
4444 }
4545
46- return true ;
46+ return $ this -> hasChildren () ;
4747 }
4848
4949 public function isMixedShape (): bool
@@ -60,6 +60,11 @@ public function isMixedShape(): bool
6060
6161 return false ;
6262 }
63+
64+ public function hasChildren (): bool
65+ {
66+ return count ($ this ->children ) > 0 ;
67+ }
6368}
6469
6570final class WordPressTag extends WithChildren
@@ -89,6 +94,17 @@ final class WordPressTag extends WithChildren
8994 */
9095 public function format (): array
9196 {
97+ if (! $ this ->hasChildren ()) {
98+ return [
99+ sprintf (
100+ '%s %s%s ' ,
101+ $ this ->tag ,
102+ $ this ->type ,
103+ ($ this ->name !== null ) ? (' $ ' . $ this ->name ) : ''
104+ )
105+ ];
106+ }
107+
92108 if ($ this ->isMixedShape ()) {
93109 return [];
94110 }
@@ -711,6 +727,17 @@ private function getAdditionFromParam(Param $tag): ?WordPressTag
711727 return null ;
712728 }
713729
730+ $ tagDescriptionType = self ::getTypeNameFromDescription ($ tagDescription , $ tagVariableType );
731+
732+ if ($ tagDescriptionType !== null ) {
733+ $ tag = new WordPressTag ();
734+ $ tag ->tag = '@phpstan-param ' ;
735+ $ tag ->type = $ tagDescriptionType ;
736+ $ tag ->name = $ tagVariableName ;
737+
738+ return $ tag ;
739+ }
740+
714741 $ elements = self ::getElementsFromDescription ($ tagDescription , true );
715742
716743 if (count ($ elements ) === 0 ) {
@@ -746,6 +773,16 @@ private function getAdditionFromReturn(Return_ $tag): ?WordPressTag
746773 return null ;
747774 }
748775
776+ $ tagDescriptionType = self ::getTypeNameFromDescription ($ tagDescription , $ tagVariableType );
777+
778+ if ($ tagDescriptionType !== null ) {
779+ $ tag = new WordPressTag ();
780+ $ tag ->tag = '@phpstan-return ' ;
781+ $ tag ->type = $ tagDescriptionType ;
782+
783+ return $ tag ;
784+ }
785+
749786 $ elements = self ::getElementsFromDescription ($ tagDescription , false );
750787
751788 if (count ($ elements ) === 0 ) {
@@ -796,6 +833,54 @@ private static function getAdditionFromVar(Var_ $tag): ?WordPressTag
796833 return $ tag ;
797834 }
798835
836+ private static function getTypeNameFromDescription (Description $ tagVariableDescription , Type $ tagVariableType ): ?string
837+ {
838+ if (!($ tagVariableType instanceof \phpDocumentor \Reflection \Types \String_)) {
839+ return null ;
840+ }
841+
842+ return self ::getTypeNameFromDescriptionString ($ tagVariableDescription ->__toString ());
843+ }
844+
845+ private static function getTypeNameFromDescriptionString (string $ tagDescription = null ): ?string
846+ {
847+ if ($ tagDescription === null ) {
848+ return null ;
849+ }
850+
851+ $ fullDescription = str_replace ("\n" , ' ' , $ tagDescription );
852+
853+ /**
854+ * This matches phrases that contain a list of two or more single-quoted strings, with the last
855+ * item separated by 'or' or 'and'. The Oxford comma is optional. For example:
856+ *
857+ * - Either 'am', 'pm', 'AM', or 'PM'
858+ * - Accepts 'comment' or 'term'
859+ * - Either 'plugin' or 'theme'
860+ * - Accepts 'big', or 'little'.
861+ * - One of 'default', 'theme', or 'custom'
862+ * - Either 'network-active', 'active' or 'inactive'
863+ * - : 'top' or 'bottom'
864+ */
865+ $ matched = preg_match ("#(?>returns|either|one of|accepts|values are|:) ('.+'),? (?>or|and) '([^']+)'#i " , $ fullDescription , $ matches );
866+
867+ if (! $ matched ) {
868+ return null ;
869+ }
870+
871+ list (, $ items , $ final ) = $ matches ;
872+
873+ // Pluck out phrases between single quotes, so messy sentences are handled:
874+ preg_match_all ("#'([^']+)'# " , $ items , $ matches );
875+
876+ list (,$ accepted ) = $ matches ;
877+
878+ // Append the final item:
879+ $ accepted [] = $ final ;
880+
881+ return "' " . implode ("'|' " , $ accepted ) . "' " ;
882+ }
883+
799884 private static function getTypeNameFromType (Type $ tagVariableType ): ?string
800885 {
801886 return self ::getTypeNameFromString ($ tagVariableType ->__toString ());
0 commit comments