@@ -1357,13 +1357,13 @@ public function resolveName(Name $name): string
13571357 $ originalClass = (string ) $ name ;
13581358 $ lowerClass = strtolower ($ originalClass );
13591359
1360- $ bindScopeClassName = $ this ->resolveClosureBindScopeClassName ($ name );
1361- if ($ bindScopeClassName !== null ) {
1360+ $ bindScopeClassReflection = $ this ->resolveClosureBindScopeClass ($ name );
1361+ if ($ bindScopeClassReflection !== null ) {
13621362 if (in_array ($ lowerClass , ['self ' , 'static ' ], true )) {
1363- return $ bindScopeClassName ;
1363+ return $ bindScopeClassReflection -> getName () ;
13641364 }
1365- if ($ lowerClass === 'parent ' && $ this -> reflectionProvider -> hasClass ( $ bindScopeClassName ) ) {
1366- $ parentClassReflection = $ this -> reflectionProvider -> getClass ( $ bindScopeClassName ) ->getParentClass ();
1365+ if ($ lowerClass === 'parent ' ) {
1366+ $ parentClassReflection = $ bindScopeClassReflection ->getParentClass ();
13671367 if ($ parentClassReflection !== null ) {
13681368 return $ parentClassReflection ->getName ();
13691369 }
@@ -1393,10 +1393,10 @@ public function resolveName(Name $name): string
13931393 /** @api */
13941394 public function resolveTypeByName (Name $ name ): TypeWithClassName
13951395 {
1396- $ bindScopeClassName = $ this ->resolveClosureBindScopeClassName ($ name );
1396+ $ bindScopeClassReflection = $ this ->resolveClosureBindScopeClass ($ name );
13971397 if ($ name ->toLowerString () === 'static ' ) {
1398- if ($ bindScopeClassName !== null && $ this -> reflectionProvider -> hasClass ( $ bindScopeClassName ) ) {
1399- return new StaticType ($ this -> reflectionProvider -> getClass ( $ bindScopeClassName ) );
1398+ if ($ bindScopeClassReflection !== null ) {
1399+ return new StaticType ($ bindScopeClassReflection );
14001400 }
14011401 if ($ this ->isInClass ()) {
14021402 if ($ this ->inClosureBindScopeClasses !== [] && $ this ->inClosureBindScopeClasses !== ['static ' ]) {
@@ -1410,7 +1410,7 @@ public function resolveTypeByName(Name $name): TypeWithClassName
14101410 }
14111411
14121412 $ originalClass = $ this ->resolveName ($ name );
1413- if ($ bindScopeClassName !== null && $ this ->reflectionProvider ->hasClass ($ originalClass )) {
1413+ if ($ bindScopeClassReflection !== null && $ this ->reflectionProvider ->hasClass ($ originalClass )) {
14141414 return new ObjectType ($ originalClass );
14151415 }
14161416 if ($ this ->isInClass ()) {
@@ -1432,31 +1432,25 @@ public function resolveTypeByName(Name $name): TypeWithClassName
14321432 }
14331433
14341434 /**
1435- * Resolves the class name that a `self`/`parent`/`static` node is bound to by a
1436- * surrounding `Closure::bind()` call, annotated by {@see ClosureBindArgVisitor}. Returns
1437- * null when the node is not inside a bound closure or the bind scope argument does not
1438- * resolve to a single known class (e.g. the default "static" scope). This works even
1439- * outside a class context, because the type of a bound closure's body is inferred in the
1440- * enclosing scope where the closure-bind scope classes are not otherwise available.
1435+ * Resolves the class a `self`/`parent`/`static` node is bound to by a surrounding
1436+ * `Closure::bind()` call, annotated by {@see ClosureBindArgVisitor} and resolved by
1437+ * {@see ClosureBindScopeResolver}. Returns null when the node is not inside a bound
1438+ * closure or the bind scope argument does not resolve to a single known class (e.g.
1439+ * the default "static" scope).
1440+ *
1441+ * This complements enterClosureBind() and inClosureBindScopeClasses: that scope-based
1442+ * path owns member accessibility while the closure body is being processed, but cannot
1443+ * reach this case, because the body's type is inferred in the enclosing scope where the
1444+ * closure-bind scope classes are not available (including outside any class).
14411445 */
1442- /** @return non-empty-string|null */
1443- private function resolveClosureBindScopeClassName (Name $ name ): ?string
1446+ private function resolveClosureBindScopeClass (Name $ name ): ?ClassReflection
14441447 {
1448+ // Cheap pre-check so the common path stays free of a container lookup.
14451449 if (!$ name ->hasAttribute (ClosureBindArgVisitor::SCOPE_ATTRIBUTE_NAME )) {
14461450 return null ;
14471451 }
14481452
1449- $ scopeArg = $ name ->getAttribute (ClosureBindArgVisitor::SCOPE_ATTRIBUTE_NAME );
1450- if (!$ scopeArg instanceof Expr) {
1451- return null ;
1452- }
1453-
1454- $ objectClassNames = $ this ->getType ($ scopeArg )->getClassStringObjectType ()->getObjectClassNames ();
1455- if (count ($ objectClassNames ) !== 1 ) {
1456- return null ;
1457- }
1458-
1459- return $ objectClassNames [0 ];
1453+ return $ this ->container ->getByType (ClosureBindScopeResolver::class)->resolveScopeClass ($ this , $ name );
14601454 }
14611455
14621456 /**
0 commit comments