@@ -1436,13 +1436,13 @@ public function resolveName(Name $name): string
14361436 $ originalClass = (string ) $ name ;
14371437 $ lowerClass = strtolower ($ originalClass );
14381438
1439- $ bindScopeClassName = $ this ->resolveClosureBindScopeClassName ($ name );
1440- if ($ bindScopeClassName !== null ) {
1439+ $ bindScopeClassReflection = $ this ->resolveClosureBindScopeClass ($ name );
1440+ if ($ bindScopeClassReflection !== null ) {
14411441 if (in_array ($ lowerClass , ['self ' , 'static ' ], true )) {
1442- return $ bindScopeClassName ;
1442+ return $ bindScopeClassReflection -> getName () ;
14431443 }
1444- if ($ lowerClass === 'parent ' && $ this -> reflectionProvider -> hasClass ( $ bindScopeClassName ) ) {
1445- $ parentClassReflection = $ this -> reflectionProvider -> getClass ( $ bindScopeClassName ) ->getParentClass ();
1444+ if ($ lowerClass === 'parent ' ) {
1445+ $ parentClassReflection = $ bindScopeClassReflection ->getParentClass ();
14461446 if ($ parentClassReflection !== null ) {
14471447 return $ parentClassReflection ->getName ();
14481448 }
@@ -1472,10 +1472,10 @@ public function resolveName(Name $name): string
14721472 /** @api */
14731473 public function resolveTypeByName (Name $ name ): TypeWithClassName
14741474 {
1475- $ bindScopeClassName = $ this ->resolveClosureBindScopeClassName ($ name );
1475+ $ bindScopeClassReflection = $ this ->resolveClosureBindScopeClass ($ name );
14761476 if ($ name ->toLowerString () === 'static ' ) {
1477- if ($ bindScopeClassName !== null && $ this -> reflectionProvider -> hasClass ( $ bindScopeClassName ) ) {
1478- return new StaticType ($ this -> reflectionProvider -> getClass ( $ bindScopeClassName ) );
1477+ if ($ bindScopeClassReflection !== null ) {
1478+ return new StaticType ($ bindScopeClassReflection );
14791479 }
14801480 if ($ this ->isInClass ()) {
14811481 if ($ this ->inClosureBindScopeClasses !== [] && $ this ->inClosureBindScopeClasses !== ['static ' ]) {
@@ -1489,7 +1489,7 @@ public function resolveTypeByName(Name $name): TypeWithClassName
14891489 }
14901490
14911491 $ originalClass = $ this ->resolveName ($ name );
1492- if ($ bindScopeClassName !== null && $ this ->reflectionProvider ->hasClass ($ originalClass )) {
1492+ if ($ bindScopeClassReflection !== null && $ this ->reflectionProvider ->hasClass ($ originalClass )) {
14931493 return new ObjectType ($ originalClass );
14941494 }
14951495 if ($ this ->isInClass ()) {
@@ -1511,31 +1511,25 @@ public function resolveTypeByName(Name $name): TypeWithClassName
15111511 }
15121512
15131513 /**
1514- * Resolves the class name that a `self`/`parent`/`static` node is bound to by a
1515- * surrounding `Closure::bind()` call, annotated by {@see ClosureBindArgVisitor}. Returns
1516- * null when the node is not inside a bound closure or the bind scope argument does not
1517- * resolve to a single known class (e.g. the default "static" scope). This works even
1518- * outside a class context, because the type of a bound closure's body is inferred in the
1519- * enclosing scope where the closure-bind scope classes are not otherwise available.
1514+ * Resolves the class a `self`/`parent`/`static` node is bound to by a surrounding
1515+ * `Closure::bind()` call, annotated by {@see ClosureBindArgVisitor} and resolved by
1516+ * {@see ClosureBindScopeResolver}. Returns null when the node is not inside a bound
1517+ * closure or the bind scope argument does not resolve to a single known class (e.g.
1518+ * the default "static" scope).
1519+ *
1520+ * This complements enterClosureBind() and inClosureBindScopeClasses: that scope-based
1521+ * path owns member accessibility while the closure body is being processed, but cannot
1522+ * reach this case, because the body's type is inferred in the enclosing scope where the
1523+ * closure-bind scope classes are not available (including outside any class).
15201524 */
1521- /** @return non-empty-string|null */
1522- private function resolveClosureBindScopeClassName (Name $ name ): ?string
1525+ private function resolveClosureBindScopeClass (Name $ name ): ?ClassReflection
15231526 {
1527+ // Cheap pre-check so the common path stays free of a container lookup.
15241528 if (!$ name ->hasAttribute (ClosureBindArgVisitor::SCOPE_ATTRIBUTE_NAME )) {
15251529 return null ;
15261530 }
15271531
1528- $ scopeArg = $ name ->getAttribute (ClosureBindArgVisitor::SCOPE_ATTRIBUTE_NAME );
1529- if (!$ scopeArg instanceof Expr) {
1530- return null ;
1531- }
1532-
1533- $ objectClassNames = $ this ->getType ($ scopeArg )->getClassStringObjectType ()->getObjectClassNames ();
1534- if (count ($ objectClassNames ) !== 1 ) {
1535- return null ;
1536- }
1537-
1538- return $ objectClassNames [0 ];
1532+ return $ this ->container ->getByType (ClosureBindScopeResolver::class)->resolveScopeClass ($ this , $ name );
15391533 }
15401534
15411535 /**
0 commit comments