Skip to content

Commit 0185100

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 c2447f5 commit 0185100

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

Comments
 (0)