1313use phpDocumentor \Reflection \DocBlock \Tags \Generic ;
1414use phpDocumentor \Reflection \DocBlock \Tags \InvalidTag ;
1515use phpDocumentor \Reflection \DocBlock \Tags \Since ;
16+ use phpDocumentor \Reflection \DocBlock \Tags \Template ;
17+ use phpDocumentor \Reflection \FqsenResolver ;
1618use phpDocumentor \Reflection \Php \Class_ ;
1719use phpDocumentor \Reflection \Php \Constant ;
1820use phpDocumentor \Reflection \Php \Interface_ ;
1921use phpDocumentor \Reflection \Php \Method ;
2022use phpDocumentor \Reflection \Php \Property ;
2123use phpDocumentor \Reflection \Php \Trait_ ;
24+ use phpDocumentor \Reflection \TypeResolver ;
2225use yii \base \BaseObject ;
2326use yii \helpers \StringHelper ;
2427
3235 */
3336class BaseDoc extends BaseObject
3437{
38+ private const PHPSTAN_TYPE_ANNOTATION_NAME = 'phpstan-type ' ;
39+ private const PSALM_TYPE_ANNOTATION_NAME = 'psalm-type ' ;
40+
41+ private const PHPSTAN_IMPORT_TYPE_ANNOTATION_NAME = 'phpstan-import-type ' ;
42+ private const PSALM_IMPORT_TYPE_ANNOTATION_NAME = 'psalm-import-type ' ;
43+
3544 private const INHERITDOC_TAG_NAME = 'inheritdoc ' ;
3645 private const TODO_TAG_NAME = 'todo ' ;
3746
3847 /**
3948 * @var \phpDocumentor\Reflection\Types\Context|null
4049 */
4150 public $ phpDocContext ;
51+ /**
52+ * @var string|null
53+ */
4254 public $ name ;
55+ /**
56+ * @var string|null
57+ */
4358 public $ fullName ;
4459 public $ sourceFile ;
4560 public $ startLine ;
@@ -64,6 +79,30 @@ class BaseDoc extends BaseObject
6479 * @var Generic[]
6580 */
6681 public $ todos = [];
82+ /**
83+ * @var array<string, Template>
84+ */
85+ public $ templates = [];
86+ /**
87+ * @var self|null
88+ */
89+ public $ parent = null ;
90+ /**
91+ * @var array<string, PseudoTypeDoc>
92+ */
93+ public array $ phpStanTypes = [];
94+ /**
95+ * @var array<string, PseudoTypeDoc>
96+ */
97+ public array $ psalmTypes = [];
98+ /**
99+ * @var array<string, PseudoTypeImportDoc>
100+ */
101+ public array $ phpStanTypeImports = [];
102+ /**
103+ * @var array<string, PseudoTypeImportDoc>
104+ */
105+ public array $ psalmTypeImports = [];
67106
68107 /**
69108 * Checks if doc has tag of a given name
@@ -126,18 +165,24 @@ public function getPackageName()
126165 }
127166
128167 /**
168+ * @param self|null $parent
129169 * @param Class_|Method|Trait_|Interface_|Property|Constant|null $reflector
130170 * @param Context|null $context
131171 * @param array $config
132172 */
133- public function __construct ($ reflector = null , $ context = null , $ config = [])
173+ public function __construct ($ parent = null , $ reflector = null , $ context = null , $ config = [])
134174 {
135175 parent ::__construct ($ config );
136176
177+ $ this ->parent = $ parent ;
178+
137179 if ($ reflector === null ) {
138180 return ;
139181 }
140182
183+ $ fqsenResolver = new FqsenResolver ();
184+ $ typeResolver = new TypeResolver ($ fqsenResolver );
185+
141186 // base properties
142187 $ this ->fullName = trim ((string ) $ reflector ->getFqsen (), '\\() ' );
143188
@@ -160,7 +205,7 @@ public function __construct($reflector = null, $context = null, $config = [])
160205 return ;
161206 }
162207
163- $ this ->shortDescription = StringHelper::mb_ucfirst ($ docBlock ->getSummary ());;
208+ $ this ->shortDescription = StringHelper::mb_ucfirst ($ docBlock ->getSummary ());
164209 if (empty ($ this ->shortDescription ) && !($ this instanceof PropertyDoc) && $ context !== null && !$ docBlock ->getTagsByName (self ::INHERITDOC_TAG_NAME )) {
165210 $ context ->warnings [] = [
166211 'line ' => $ this ->startLine ,
@@ -192,9 +237,57 @@ public function __construct($reflector = null, $context = null, $config = [])
192237 $ this ->deprecatedSince = $ tag ->getVersion ();
193238 $ this ->deprecatedReason = (string ) $ tag ->getDescription ();
194239 unset($ this ->tags [$ i ]);
195- } elseif ($ tag instanceof Generic && $ tag ->getName () === self ::TODO_TAG_NAME ) {
196- $ this ->todos [] = $ tag ;
240+ } elseif ($ tag instanceof Template) {
241+ $ fqsen = $ fqsenResolver ->resolve ($ tag ->getTemplateName (), $ this ->phpDocContext );
242+ $ this ->templates [(string ) $ fqsen ] = $ tag ;
197243 unset($ this ->tags [$ i ]);
244+ } elseif ($ tag instanceof Generic) {
245+ if ($ tag ->getName () === self ::TODO_TAG_NAME ) {
246+ $ this ->todos [] = $ tag ;
247+ unset($ this ->tags [$ i ]);
248+ } elseif ($ tag ->getName () === self ::PHPSTAN_TYPE_ANNOTATION_NAME ) {
249+ $ tagData = explode (' ' , trim ($ tag ->getDescription ()), 2 );
250+ $ phpStanType = new PseudoTypeDoc (
251+ PseudoTypeDoc::TYPE_PHPSTAN ,
252+ $ this ,
253+ trim ($ tagData [0 ]),
254+ $ typeResolver ->resolve (trim ($ tagData [1 ]), $ this ->phpDocContext )
255+ );
256+ $ fqsen = $ fqsenResolver ->resolve ($ phpStanType ->name , $ this ->phpDocContext );
257+ $ this ->phpStanTypes [(string ) $ fqsen ] = $ phpStanType ;
258+ unset($ this ->tags [$ i ]);
259+ } elseif ($ tag ->getName () === self ::PSALM_TYPE_ANNOTATION_NAME ) {
260+ $ tagData = explode ('= ' , trim ($ tag ->getDescription ()), 2 );
261+ $ psalmType = new PseudoTypeDoc (
262+ PseudoTypeDoc::TYPE_PSALM ,
263+ $ this ,
264+ trim ($ tagData [0 ]),
265+ $ typeResolver ->resolve (trim ($ tagData [1 ]), $ this ->phpDocContext )
266+ );
267+ $ fqsen = $ fqsenResolver ->resolve ($ psalmType ->name , $ this ->phpDocContext );
268+ $ this ->psalmTypes [(string ) $ fqsen ] = $ psalmType ;
269+ unset($ this ->tags [$ i ]);
270+ } elseif ($ tag ->getName () === self ::PHPSTAN_IMPORT_TYPE_ANNOTATION_NAME ) {
271+ $ tagData = explode (' from ' , trim ($ tag ->getDescription ()), 2 );
272+ $ phpStanTypeImport = new PseudoTypeImportDoc (
273+ PseudoTypeImportDoc::TYPE_PHPSTAN ,
274+ trim ($ tagData [0 ]),
275+ $ fqsenResolver ->resolve (trim ($ tagData [1 ]), $ this ->phpDocContext )
276+ );
277+ $ fqsen = $ fqsenResolver ->resolve ($ phpStanTypeImport ->typeName , $ this ->phpDocContext );
278+ $ this ->phpStanTypeImports [(string ) $ fqsen ] = $ phpStanTypeImport ;
279+ unset($ this ->tags [$ i ]);
280+ } elseif ($ tag ->getName () === self ::PSALM_IMPORT_TYPE_ANNOTATION_NAME ) {
281+ $ tagData = explode (' from ' , trim ($ tag ->getDescription ()), 2 );
282+ $ psalmTypeImport = new PseudoTypeImportDoc (
283+ PseudoTypeImportDoc::TYPE_PSALM ,
284+ trim ($ tagData [0 ]),
285+ $ fqsenResolver ->resolve (trim ($ tagData [1 ]), $ this ->phpDocContext )
286+ );
287+ $ fqsen = $ fqsenResolver ->resolve ($ psalmTypeImport ->typeName , $ this ->phpDocContext );
288+ $ this ->psalmTypeImports [(string ) $ fqsen ] = $ psalmTypeImport ;
289+ unset($ this ->tags [$ i ]);
290+ }
198291 } elseif ($ tag instanceof InvalidTag && $ context !== null ) {
199292 $ exception = $ tag ->getException ();
200293 $ message = 'Invalid tag: ' . $ tag ->render () . '. ' ;
0 commit comments