Skip to content

Commit 5444d4a

Browse files
committed
Delegate the bind-scope resolution in MutatingScope to ClosureBindScopeResolver
MutatingScope::resolveClosureBindScopeClassName() repeated what ClosureBindScopeResolver::resolveScopeClass() already does. Fetch the resolver from the container behind the cheap attribute pre-check, so the duplication is gone and the hot path stays free of a container lookup. Also merge the method's two doc blocks into one and note how this annotation-based path divides the work with the scope-based enterClosureBind() one.
1 parent ffbfec8 commit 5444d4a

1 file changed

Lines changed: 22 additions & 28 deletions

File tree

‎src/Analyser/MutatingScope.php‎

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)