99use PHPStan \Broker \AnonymousClassNameHelper ;
1010use PHPStan \File \FileHelper ;
1111use PHPStan \Parser \Parser ;
12+ use PHPStan \Parser \PropertyHookNameVisitor ;
1213use PHPStan \PhpDoc \PhpDocNodeResolver ;
1314use PHPStan \PhpDoc \PhpDocStringResolver ;
1415use PHPStan \PhpDoc \ResolvedPhpDocBlock ;
@@ -279,6 +280,11 @@ function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodA
279280 }
280281 } elseif ($ node instanceof Node \Stmt \Function_) {
281282 $ functionStack [] = ltrim (sprintf ('%s \\%s ' , $ namespace , $ node ->name ->name ), '\\' );
283+ } elseif ($ node instanceof Node \PropertyHook) {
284+ $ propertyName = $ node ->getAttribute (PropertyHookNameVisitor::ATTRIBUTE_NAME );
285+ if ($ propertyName !== null ) {
286+ $ functionStack [] = sprintf ('$%s::%s ' , $ propertyName , $ node ->name ->toString ());
287+ }
282288 }
283289
284290 $ className = $ classStack [count ($ classStack ) - 1 ] ?? null ;
@@ -291,6 +297,17 @@ function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodA
291297 $ phpDocNodeMap [$ nameScopeKey ] = $ this ->phpDocStringResolver ->resolve ($ docComment );
292298 }
293299
300+ return null ;
301+ } elseif ($ node instanceof Node \PropertyHook) {
302+ $ propertyName = $ node ->getAttribute (PropertyHookNameVisitor::ATTRIBUTE_NAME );
303+ if ($ propertyName !== null ) {
304+ $ docComment = GetLastDocComment::forNode ($ node );
305+ if ($ docComment !== null ) {
306+ $ nameScopeKey = $ this ->getNameScopeKey ($ originalClassFileName , $ className , $ lookForTrait , $ functionName );
307+ $ phpDocNodeMap [$ nameScopeKey ] = $ this ->phpDocStringResolver ->resolve ($ docComment );
308+ }
309+ }
310+
294311 return null ;
295312 }
296313
@@ -376,6 +393,15 @@ static function (Node $node) use (&$namespace, &$functionStack, &$classStack): v
376393 }
377394
378395 array_pop ($ functionStack );
396+ } elseif ($ node instanceof Node \PropertyHook) {
397+ $ propertyName = $ node ->getAttribute (PropertyHookNameVisitor::ATTRIBUTE_NAME );
398+ if ($ propertyName !== null ) {
399+ if (count ($ functionStack ) === 0 ) {
400+ throw new ShouldNotHappenException ();
401+ }
402+
403+ array_pop ($ functionStack );
404+ }
379405 }
380406 },
381407 );
@@ -476,13 +502,19 @@ function (Node $node) use ($fileName, $lookForTrait, $phpDocNodeMap, &$traitFoun
476502 }
477503 } elseif ($ node instanceof Node \Stmt \Function_) {
478504 $ functionStack [] = ltrim (sprintf ('%s \\%s ' , $ namespace , $ node ->name ->name ), '\\' );
505+ } elseif ($ node instanceof Node \PropertyHook) {
506+ $ propertyName = $ node ->getAttribute (PropertyHookNameVisitor::ATTRIBUTE_NAME );
507+ if ($ propertyName !== null ) {
508+ $ functionStack [] = sprintf ('$%s::%s ' , $ propertyName , $ node ->name ->toString ());
509+ }
479510 }
480511
481512 $ className = $ classStack [count ($ classStack ) - 1 ] ?? null ;
482513 $ functionName = $ functionStack [count ($ functionStack ) - 1 ] ?? null ;
483514 $ nameScopeKey = $ this ->getNameScopeKey ($ originalClassFileName , $ className , $ lookForTrait , $ functionName );
484515
485516 if ($ node instanceof Node \Stmt \ClassLike || $ node instanceof Node \Stmt \ClassMethod || $ node instanceof Node \Stmt \Function_) {
517+ // property hook skipped on purpose, it does not support @template
486518 if (array_key_exists ($ nameScopeKey , $ phpDocNodeMap )) {
487519 $ phpDocNode = $ phpDocNodeMap [$ nameScopeKey ];
488520 $ typeMapStack [] = function () use ($ namespace , $ uses , $ className , $ lookForTrait , $ functionName , $ phpDocNode , $ typeMapStack , $ typeAliasStack , $ constUses ): TemplateTypeMap {
@@ -512,16 +544,20 @@ function (Node $node) use ($fileName, $lookForTrait, $phpDocNodeMap, &$traitFoun
512544 $ typeAliasesMap = $ typeAliasStack [count ($ typeAliasStack ) - 1 ] ?? [];
513545
514546 if (
515- $ node instanceof Node \Stmt
516- && !$ node instanceof Node \Stmt \Namespace_
517- && !$ node instanceof Node \Stmt \Declare_
518- && !$ node instanceof Node \Stmt \Use_
519- && !$ node instanceof Node \Stmt \GroupUse
520- && !$ node instanceof Node \Stmt \TraitUse
521- && !$ node instanceof Node \Stmt \TraitUseAdaptation
522- && !$ node instanceof Node \Stmt \InlineHTML
523- && !($ node instanceof Node \Stmt \Expression && $ node ->expr instanceof Node \Expr \Include_)
524- && !array_key_exists ($ nameScopeKey , $ nameScopeMap )
547+ (
548+ $ node instanceof Node \PropertyHook
549+ || (
550+ $ node instanceof Node \Stmt
551+ && !$ node instanceof Node \Stmt \Namespace_
552+ && !$ node instanceof Node \Stmt \Declare_
553+ && !$ node instanceof Node \Stmt \Use_
554+ && !$ node instanceof Node \Stmt \GroupUse
555+ && !$ node instanceof Node \Stmt \TraitUse
556+ && !$ node instanceof Node \Stmt \TraitUseAdaptation
557+ && !$ node instanceof Node \Stmt \InlineHTML
558+ && !($ node instanceof Node \Stmt \Expression && $ node ->expr instanceof Node \Expr \Include_)
559+ )
560+ ) && !array_key_exists ($ nameScopeKey , $ nameScopeMap )
525561 ) {
526562 $ nameScopeMap [$ nameScopeKey ] = static fn (): NameScope => new NameScope (
527563 $ namespace ,
@@ -537,6 +573,7 @@ function (Node $node) use ($fileName, $lookForTrait, $phpDocNodeMap, &$traitFoun
537573 }
538574
539575 if ($ node instanceof Node \Stmt \ClassLike || $ node instanceof Node \Stmt \ClassMethod || $ node instanceof Node \Stmt \Function_) {
576+ // property hook skipped on purpose, it does not support @template
540577 if (array_key_exists ($ nameScopeKey , $ phpDocNodeMap )) {
541578 return self ::POP_TYPE_MAP_STACK ;
542579 }
@@ -704,6 +741,15 @@ static function (Node $node, $callbackResult) use (&$namespace, &$functionStack,
704741 }
705742
706743 array_pop ($ functionStack );
744+ } elseif ($ node instanceof Node \PropertyHook) {
745+ $ propertyName = $ node ->getAttribute (PropertyHookNameVisitor::ATTRIBUTE_NAME );
746+ if ($ propertyName !== null ) {
747+ if (count ($ functionStack ) === 0 ) {
748+ throw new ShouldNotHappenException ();
749+ }
750+
751+ array_pop ($ functionStack );
752+ }
707753 }
708754 if ($ callbackResult !== self ::POP_TYPE_MAP_STACK ) {
709755 return ;
0 commit comments