@@ -1420,13 +1420,13 @@ public function resolveName(Name $name): string
14201420 $ originalClass = (string ) $ name ;
14211421 $ lowerClass = strtolower ($ originalClass );
14221422
1423- $ bindScopeClassName = $ this ->resolveClosureBindScopeClassName ($ name );
1424- if ($ bindScopeClassName !== null ) {
1423+ $ bindScopeClassReflection = $ this ->resolveClosureBindScopeClass ($ name );
1424+ if ($ bindScopeClassReflection !== null ) {
14251425 if (in_array ($ lowerClass , ['self ' , 'static ' ], true )) {
1426- return $ bindScopeClassName ;
1426+ return $ bindScopeClassReflection -> getName () ;
14271427 }
1428- if ($ lowerClass === 'parent ' && $ this -> reflectionProvider -> hasClass ( $ bindScopeClassName ) ) {
1429- $ parentClassReflection = $ this -> reflectionProvider -> getClass ( $ bindScopeClassName ) ->getParentClass ();
1428+ if ($ lowerClass === 'parent ' ) {
1429+ $ parentClassReflection = $ bindScopeClassReflection ->getParentClass ();
14301430 if ($ parentClassReflection !== null ) {
14311431 return $ parentClassReflection ->getName ();
14321432 }
@@ -1456,10 +1456,10 @@ public function resolveName(Name $name): string
14561456 /** @api */
14571457 public function resolveTypeByName (Name $ name ): TypeWithClassName
14581458 {
1459- $ bindScopeClassName = $ this ->resolveClosureBindScopeClassName ($ name );
1459+ $ bindScopeClassReflection = $ this ->resolveClosureBindScopeClass ($ name );
14601460 if ($ name ->toLowerString () === 'static ' ) {
1461- if ($ bindScopeClassName !== null && $ this -> reflectionProvider -> hasClass ( $ bindScopeClassName ) ) {
1462- return new StaticType ($ this -> reflectionProvider -> getClass ( $ bindScopeClassName ) );
1461+ if ($ bindScopeClassReflection !== null ) {
1462+ return new StaticType ($ bindScopeClassReflection );
14631463 }
14641464 if ($ this ->isInClass ()) {
14651465 if ($ this ->inClosureBindScopeClasses !== [] && $ this ->inClosureBindScopeClasses !== ['static ' ]) {
@@ -1473,7 +1473,7 @@ public function resolveTypeByName(Name $name): TypeWithClassName
14731473 }
14741474
14751475 $ originalClass = $ this ->resolveName ($ name );
1476- if ($ bindScopeClassName !== null && $ this ->reflectionProvider ->hasClass ($ originalClass )) {
1476+ if ($ bindScopeClassReflection !== null && $ this ->reflectionProvider ->hasClass ($ originalClass )) {
14771477 return new ObjectType ($ originalClass );
14781478 }
14791479 if ($ this ->isInClass ()) {
@@ -1495,31 +1495,25 @@ public function resolveTypeByName(Name $name): TypeWithClassName
14951495 }
14961496
14971497 /**
1498- * Resolves the class name that a `self`/`parent`/`static` node is bound to by a
1499- * surrounding `Closure::bind()` call, annotated by {@see ClosureBindArgVisitor}. Returns
1500- * null when the node is not inside a bound closure or the bind scope argument does not
1501- * resolve to a single known class (e.g. the default "static" scope). This works even
1502- * outside a class context, because the type of a bound closure's body is inferred in the
1503- * enclosing scope where the closure-bind scope classes are not otherwise available.
1498+ * Resolves the class a `self`/`parent`/`static` node is bound to by a surrounding
1499+ * `Closure::bind()` call, annotated by {@see ClosureBindArgVisitor} and resolved by
1500+ * {@see ClosureBindScopeResolver}. Returns null when the node is not inside a bound
1501+ * closure or the bind scope argument does not resolve to a single known class (e.g.
1502+ * the default "static" scope).
1503+ *
1504+ * This complements enterClosureBind() and inClosureBindScopeClasses: that scope-based
1505+ * path owns member accessibility while the closure body is being processed, but cannot
1506+ * reach this case, because the body's type is inferred in the enclosing scope where the
1507+ * closure-bind scope classes are not available (including outside any class).
15041508 */
1505- /** @return non-empty-string|null */
1506- private function resolveClosureBindScopeClassName (Name $ name ): ?string
1509+ private function resolveClosureBindScopeClass (Name $ name ): ?ClassReflection
15071510 {
1511+ // Cheap pre-check so the common path stays free of a container lookup.
15081512 if (!$ name ->hasAttribute (ClosureBindArgVisitor::SCOPE_ATTRIBUTE_NAME )) {
15091513 return null ;
15101514 }
15111515
1512- $ scopeArg = $ name ->getAttribute (ClosureBindArgVisitor::SCOPE_ATTRIBUTE_NAME );
1513- if (!$ scopeArg instanceof Expr) {
1514- return null ;
1515- }
1516-
1517- $ objectClassNames = $ this ->getType ($ scopeArg )->getClassStringObjectType ()->getObjectClassNames ();
1518- if (count ($ objectClassNames ) !== 1 ) {
1519- return null ;
1520- }
1521-
1522- return $ objectClassNames [0 ];
1516+ return $ this ->container ->getByType (ClosureBindScopeResolver::class)->resolveScopeClass ($ this , $ name );
15231517 }
15241518
15251519 /**
0 commit comments