Skip to content

Commit 79b41ef

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 48836fc commit 79b41ef

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
@@ -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

Comments
 (0)