Repository navigation
Conversation
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Jul 8, 2026
Annotate special class-name nodes (self/parent/static) that appear inside a Closure::bind() call with the bind scope argument via ClosureBindArgVisitor, and resolve the class constant type against that bound class in ClassConstFetchHandler. Reconstruction of PR phpstan#4081 (closure-bind-scope) rebased onto 2.2.x: the original MutatingScope::getType() ClassConstFetch branch no longer exists after expression handling was split into ExprHandler services.
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Jul 8, 2026
… properties and instantiation Extend the Closure::bind() scope binding beyond class constants to the natural remaining surface: - MutatingScope::resolveName()/resolveTypeByName() now honour the bind scope annotated on self/parent/static class-name nodes, so static method-call return types, static property types and `new self/parent/static` resolve against the bound class even outside a class context (the closure body's type is inferred in the enclosing scope where the closure-bind scope classes are otherwise absent). - StaticMethodCallCheck, AccessStaticPropertiesCheck and InstantiationRule no longer report "outside of class scope" for self/parent/static inside a bound closure, resolving accessibility against the bound class. - ClosureBindArgVisitor now only rescopes the closure body (1st argument), not the $newThis/$newScope arguments, avoiding a self-referential scope annotation on a `self::class` scope argument. Continues the reconstruction of PR phpstan#4081 on 2.2.x.
zonuexe
force-pushed
the
fix/bind-class-scope
branch
from
July 8, 2026 12:46
b3c42c8 to
1fb897e
Compare
Closure::bind() scope binding improvements
zonuexe
marked this pull request as ready for review
July 8, 2026 12:49
Collaborator
|
This pull request has been marked as ready for review. |
zonuexe
marked this pull request as draft
July 14, 2026 03:23
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Jul 14, 2026
Annotate special class-name nodes (self/parent/static) that appear inside a Closure::bind() call with the bind scope argument via ClosureBindArgVisitor, and resolve the class constant type against that bound class in ClassConstFetchHandler. Reconstruction of PR phpstan#4081 (closure-bind-scope) rebased onto 2.2.x: the original MutatingScope::getType() ClassConstFetch branch no longer exists after expression handling was split into ExprHandler services.
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Jul 14, 2026
… properties and instantiation Extend the Closure::bind() scope binding beyond class constants to the natural remaining surface: - MutatingScope::resolveName()/resolveTypeByName() now honour the bind scope annotated on self/parent/static class-name nodes, so static method-call return types, static property types and `new self/parent/static` resolve against the bound class even outside a class context (the closure body's type is inferred in the enclosing scope where the closure-bind scope classes are otherwise absent). - StaticMethodCallCheck, AccessStaticPropertiesCheck and InstantiationRule no longer report "outside of class scope" for self/parent/static inside a bound closure, resolving accessibility against the bound class. - ClosureBindArgVisitor now only rescopes the closure body (1st argument), not the $newThis/$newScope arguments, avoiding a self-referential scope annotation on a `self::class` scope argument. Continues the reconstruction of PR phpstan#4081 on 2.2.x.
zonuexe
force-pushed
the
fix/bind-class-scope
branch
from
July 14, 2026 03:29
1fb897e to
8d9f662
Compare
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Jul 14, 2026
Annotate special class-name nodes (self/parent/static) that appear inside a Closure::bind() call with the bind scope argument via ClosureBindArgVisitor, and resolve the class constant type against that bound class in ClassConstFetchHandler. Reconstruction of PR phpstan#4081 (closure-bind-scope) rebased onto 2.2.x: the original MutatingScope::getType() ClassConstFetch branch no longer exists after expression handling was split into ExprHandler services.
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Jul 14, 2026
… properties and instantiation Extend the Closure::bind() scope binding beyond class constants to the natural remaining surface: - MutatingScope::resolveName()/resolveTypeByName() now honour the bind scope annotated on self/parent/static class-name nodes, so static method-call return types, static property types and `new self/parent/static` resolve against the bound class even outside a class context (the closure body's type is inferred in the enclosing scope where the closure-bind scope classes are otherwise absent). - StaticMethodCallCheck, AccessStaticPropertiesCheck and InstantiationRule no longer report "outside of class scope" for self/parent/static inside a bound closure, resolving accessibility against the bound class. - ClosureBindArgVisitor now only rescopes the closure body (1st argument), not the $newThis/$newScope arguments, avoiding a self-referential scope annotation on a `self::class` scope argument. Continues the reconstruction of PR phpstan#4081 on 2.2.x.
zonuexe
force-pushed
the
fix/bind-class-scope
branch
from
July 14, 2026 13:23
8d9f662 to
0b15116
Compare
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Jul 20, 2026
Annotate special class-name nodes (self/parent/static) that appear inside a Closure::bind() call with the bind scope argument via ClosureBindArgVisitor, and resolve the class constant type against that bound class in ClassConstFetchHandler. Reconstruction of PR phpstan#4081 (closure-bind-scope) rebased onto 2.2.x: the original MutatingScope::getType() ClassConstFetch branch no longer exists after expression handling was split into ExprHandler services.
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Jul 20, 2026
… properties and instantiation Extend the Closure::bind() scope binding beyond class constants to the natural remaining surface: - MutatingScope::resolveName()/resolveTypeByName() now honour the bind scope annotated on self/parent/static class-name nodes, so static method-call return types, static property types and `new self/parent/static` resolve against the bound class even outside a class context (the closure body's type is inferred in the enclosing scope where the closure-bind scope classes are otherwise absent). - StaticMethodCallCheck, AccessStaticPropertiesCheck and InstantiationRule no longer report "outside of class scope" for self/parent/static inside a bound closure, resolving accessibility against the bound class. - ClosureBindArgVisitor now only rescopes the closure body (1st argument), not the $newThis/$newScope arguments, avoiding a self-referential scope annotation on a `self::class` scope argument. Continues the reconstruction of PR phpstan#4081 on 2.2.x.
zonuexe
force-pushed
the
fix/bind-class-scope
branch
from
July 20, 2026 05:15
0b15116 to
58a5dde
Compare
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Jul 20, 2026
… properties and instantiation Extend the Closure::bind() scope binding beyond class constants to the natural remaining surface: - MutatingScope::resolveName()/resolveTypeByName() now honour the bind scope annotated on self/parent/static class-name nodes, so static method-call return types, static property types and `new self/parent/static` resolve against the bound class even outside a class context (the closure body's type is inferred in the enclosing scope where the closure-bind scope classes are otherwise absent). - StaticMethodCallCheck, AccessStaticPropertiesCheck and InstantiationRule no longer report "outside of class scope" for self/parent/static inside a bound closure, resolving accessibility against the bound class. - ClosureBindArgVisitor now only rescopes the closure body (1st argument), not the $newThis/$newScope arguments, avoiding a self-referential scope annotation on a `self::class` scope argument. Continues the reconstruction of PR phpstan#4081 on 2.2.x.
zonuexe
force-pushed
the
fix/bind-class-scope
branch
from
July 20, 2026 05:33
58a5dde to
514ad90
Compare
zonuexe
marked this pull request as ready for review
July 20, 2026 05:41
Collaborator
|
This pull request has been marked as ready for review. |
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Jul 23, 2026
Annotate special class-name nodes (self/parent/static) that appear inside a Closure::bind() call with the bind scope argument via ClosureBindArgVisitor, and resolve the class constant type against that bound class in ClassConstFetchHandler. Reconstruction of PR phpstan#4081 (closure-bind-scope) rebased onto 2.2.x: the original MutatingScope::getType() ClassConstFetch branch no longer exists after expression handling was split into ExprHandler services.
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Jul 23, 2026
… properties and instantiation Extend the Closure::bind() scope binding beyond class constants to the natural remaining surface: - MutatingScope::resolveName()/resolveTypeByName() now honour the bind scope annotated on self/parent/static class-name nodes, so static method-call return types, static property types and `new self/parent/static` resolve against the bound class even outside a class context (the closure body's type is inferred in the enclosing scope where the closure-bind scope classes are otherwise absent). - StaticMethodCallCheck, AccessStaticPropertiesCheck and InstantiationRule no longer report "outside of class scope" for self/parent/static inside a bound closure, resolving accessibility against the bound class. - ClosureBindArgVisitor now only rescopes the closure body (1st argument), not the $newThis/$newScope arguments, avoiding a self-referential scope annotation on a `self::class` scope argument. Continues the reconstruction of PR phpstan#4081 on 2.2.x.
zonuexe
force-pushed
the
fix/bind-class-scope
branch
from
July 23, 2026 10:22
514ad90 to
2fc7cd8
Compare
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Aug 9, 2026
Annotate special class-name nodes (self/parent/static) that appear inside a Closure::bind() call with the bind scope argument via ClosureBindArgVisitor, and resolve the class constant type against that bound class in ClassConstFetchHandler. Reconstruction of PR phpstan#4081 (closure-bind-scope) rebased onto 2.2.x: the original MutatingScope::getType() ClassConstFetch branch no longer exists after expression handling was split into ExprHandler services.
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Aug 9, 2026
… properties and instantiation Extend the Closure::bind() scope binding beyond class constants to the natural remaining surface: - MutatingScope::resolveName()/resolveTypeByName() now honour the bind scope annotated on self/parent/static class-name nodes, so static method-call return types, static property types and `new self/parent/static` resolve against the bound class even outside a class context (the closure body's type is inferred in the enclosing scope where the closure-bind scope classes are otherwise absent). - StaticMethodCallCheck, AccessStaticPropertiesCheck and InstantiationRule no longer report "outside of class scope" for self/parent/static inside a bound closure, resolving accessibility against the bound class. - ClosureBindArgVisitor now only rescopes the closure body (1st argument), not the $newThis/$newScope arguments, avoiding a self-referential scope annotation on a `self::class` scope argument. Continues the reconstruction of PR phpstan#4081 on 2.2.x.
zonuexe
force-pushed
the
fix/bind-class-scope
branch
from
August 9, 2026 07:26
2fc7cd8 to
5c4cac0
Compare
Contributor
|
@SanderMuller please review :) |
zonuexe
force-pushed
the
fix/bind-class-scope
branch
from
August 11, 2026 13:14
c469c18 to
aa40aab
Compare
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Aug 26, 2026
Annotate special class-name nodes (self/parent/static) that appear inside a Closure::bind() call with the bind scope argument via ClosureBindArgVisitor, and resolve the class constant type against that bound class in ClassConstFetchHandler. Reconstruction of PR phpstan#4081 (closure-bind-scope) rebased onto 2.2.x: the original MutatingScope::getType() ClassConstFetch branch no longer exists after expression handling was split into ExprHandler services.
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Aug 26, 2026
… properties and instantiation Extend the Closure::bind() scope binding beyond class constants to the natural remaining surface: - MutatingScope::resolveName()/resolveTypeByName() now honour the bind scope annotated on self/parent/static class-name nodes, so static method-call return types, static property types and `new self/parent/static` resolve against the bound class even outside a class context (the closure body's type is inferred in the enclosing scope where the closure-bind scope classes are otherwise absent). - StaticMethodCallCheck, AccessStaticPropertiesCheck and InstantiationRule no longer report "outside of class scope" for self/parent/static inside a bound closure, resolving accessibility against the bound class. - ClosureBindArgVisitor now only rescopes the closure body (1st argument), not the $newThis/$newScope arguments, avoiding a self-referential scope annotation on a `self::class` scope argument. Continues the reconstruction of PR phpstan#4081 on 2.2.x.
zonuexe
force-pushed
the
fix/bind-class-scope
branch
from
August 26, 2026 04:41
aa40aab to
f443381
Compare
SanderMuller
approved these changes
Aug 26, 2026
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Sep 22, 2026
Annotate special class-name nodes (self/parent/static) that appear inside a Closure::bind() call with the bind scope argument via ClosureBindArgVisitor, and resolve the class constant type against that bound class in ClassConstFetchHandler. Reconstruction of PR phpstan#4081 (closure-bind-scope) rebased onto 2.2.x: the original MutatingScope::getType() ClassConstFetch branch no longer exists after expression handling was split into ExprHandler services.
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Sep 22, 2026
… properties and instantiation Extend the Closure::bind() scope binding beyond class constants to the natural remaining surface: - MutatingScope::resolveName()/resolveTypeByName() now honour the bind scope annotated on self/parent/static class-name nodes, so static method-call return types, static property types and `new self/parent/static` resolve against the bound class even outside a class context (the closure body's type is inferred in the enclosing scope where the closure-bind scope classes are otherwise absent). - StaticMethodCallCheck, AccessStaticPropertiesCheck and InstantiationRule no longer report "outside of class scope" for self/parent/static inside a bound closure, resolving accessibility against the bound class. - ClosureBindArgVisitor now only rescopes the closure body (1st argument), not the $newThis/$newScope arguments, avoiding a self-referential scope annotation on a `self::class` scope argument. Continues the reconstruction of PR phpstan#4081 on 2.2.x.
zonuexe
force-pushed
the
fix/bind-class-scope
branch
from
September 22, 2026 21:36
f443381 to
eb16ce0
Compare
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Sep 25, 2026
Annotate special class-name nodes (self/parent/static) that appear inside a Closure::bind() call with the bind scope argument via ClosureBindArgVisitor, and resolve the class constant type against that bound class in ClassConstFetchHandler. Reconstruction of PR phpstan#4081 (closure-bind-scope) rebased onto 2.2.x: the original MutatingScope::getType() ClassConstFetch branch no longer exists after expression handling was split into ExprHandler services.
zonuexe
added a commit
to zonuexe/phpstan-src
that referenced
this pull request
Sep 25, 2026
… properties and instantiation Extend the Closure::bind() scope binding beyond class constants to the natural remaining surface: - MutatingScope::resolveName()/resolveTypeByName() now honour the bind scope annotated on self/parent/static class-name nodes, so static method-call return types, static property types and `new self/parent/static` resolve against the bound class even outside a class context (the closure body's type is inferred in the enclosing scope where the closure-bind scope classes are otherwise absent). - StaticMethodCallCheck, AccessStaticPropertiesCheck and InstantiationRule no longer report "outside of class scope" for self/parent/static inside a bound closure, resolving accessibility against the bound class. - ClosureBindArgVisitor now only rescopes the closure body (1st argument), not the $newThis/$newScope arguments, avoiding a self-referential scope annotation on a `self::class` scope argument. Continues the reconstruction of PR phpstan#4081 on 2.2.x.
zonuexe
force-pushed
the
fix/bind-class-scope
branch
from
September 25, 2026 00:22
eb16ce0 to
73c257b
Compare
zonuexe
marked this pull request as draft
October 5, 2026 12:10
This was referenced Oct 5, 2026
zonuexe
force-pushed
the
fix/bind-class-scope
branch
2 times, most recently
from
October 5, 2026 19:34
ae94ccd to
af4dbee
Compare
The bind-scope factory captured the call as written and read $newThis and $newScope from getArgs()[1] and [2]. processArgs() already walks the normalized call, so the closure gets the factory in any argument order, but with named arguments the factory picked the wrong arguments: $this became mixed, object or a class-name string, and the class scope was lost, so accessing the scope class's private and protected members was reported. Normalize the call inside the factory as well. The reordered Args keep the same value expressions, so their stored results are still found. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ClosureBindArgVisitor marked the first argument of any Closure::bind() call with more than one argument as the bound closure. With named arguments the first argument can be $newThis - Closure::bind(newThis: $c, closure: ...) - and when it was a variable with a @param-closure-this type, ParametersAcceptorSelector narrowed the $newThis parameter to that type and reported the variable passed for it. Find the closure and $newThis arguments by position or by name, and mark the closure argument only when both are passed. A duplicated named argument does not replace the one already found, like in ArgumentsNormalizer::reorderArgs(). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…amed ParametersAcceptorSelector narrowed the $newThis parameter of Closure::bind() to a closure variable's @param-closure-this type only when the closure was the first argument. With named arguments the closure can be anywhere - Closure::bind(newThis: $o, closure: $c) - and ClosureBindArgVisitor marks it wherever it is, so look the marked argument up when the first one is named. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
MutatingScope already recorded the classes a Closure::bind() / Closure::call()
scope is bound to (inClosureBindScopeClasses: the bind factory evaluates
newScope at the call site, and entering a class or function resets them),
but honoured them for self/parent/static only inside a class. Now:
- resolveName(), resolveTypeByName() and getFunctionType() resolve
self/static to the bound class and parent to its parent, outside a class
as well as inside one; outside a class only these names follow the bound
class. getClosureBindScopeClassReflection() exposes it.
- A newScope whose class is not exactly one known class keeps the scope bound
without naming a class: a class-string, string or object of no known class
records UNKNOWN_CLOSURE_BIND_SCOPE_CLASS ('static' and null name none), and
several classes stay several. isClosureBindScopeClassAmbiguous() reports
it; self/static/parent then never fall back to the enclosing class or one
of the candidates, but to the closest class all candidates extend
(getClosureBindScopeCommonAncestor()), and type hints to mixed without one.
- A member is accessible from a scope bound to an unknown class, which may
be the class declaring it.
The native MutatingScope and StaticCallHandler mirror it, and the scope
differential walks closures bound to an unknown class, several classes,
'static' and null.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…pe class ClassConstFetchHandler took the class self/parent/static::CONST resolve against from the enclosing class only, and NewHandler / ::class built a type literally named "self" outside a class. Read the bound class off the scope instead: inside a closure bound to one class it is that class (outside a class too); bound to a class that is not exactly one known class, no class for constants, and for new / ::class the closest class all candidates extend - an object / class-string of some class without one. Both handlers' C++ twins mirror it; the walk-trace fixtures cover a bound class, an ambiguous one and named arguments. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…lass ClassConstantRule, StaticMethodCallCheck, AccessStaticPropertiesCheck and InstantiationRule reported "outside of class scope" for self/parent/static in a closure bound to a class outside any class, and checked them against the enclosing class inside one. A small SelfClassResolver helper now answers which class they are relative to - the bound class (Scope::resolveName()), else the enclosing class - and the rules check against it: "outside of class scope" only without one, "does not extend any class" for a bound class without a parent, members through Scope::resolveTypeByName() (static:: described as static(Foo)). Bound to a class that is not exactly one known class, the rules stay silent for self/parent/static. The tests cover the bound class's protected members, an object newScope, private members from a subclass scope, parent-less bound classes, undefined members, unbound and 'static'/null scopes, binds inside another class, ambiguous and unknown scopes (including member access from them), named arguments, and closure type hints resolved against the bound class. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A closure bound with Closure::bind($closure, $object, Foo::class) may call Foo's instance methods through self::/static::, also at the top level and in functions: PHP forwards the bound $this. StaticMethodCallCheck only accepted that inside a non-static method of the class. Inside a closure-bind scope with a defined $this, decide by whether $this is the method's class or a subclass of it instead. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…sure-this - A class or function declared inside a bound closure has its own self/parent/static (rule data and a walk-trace fixture). - The bind scope applies to the closure body only: the arguments of the call keep resolving against the enclosing class (regression test for phpstan/phpstan#6319, reproducer from the issue's playground sample). - A closure passed to a @param-closure-this parameter is analysed as scoped to that class, so self:: names it even outside any class. - More parser-visitor snippets with self/parent/static names inside Closure::bind() calls, which the visitor leaves alone. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
zonuexe
marked this pull request as ready for review
October 6, 2026 17:13
zonuexe
force-pushed
the
fix/bind-class-scope
branch
from
October 6, 2026 17:13
af4dbee to
c5366df
Compare
Collaborator
|
This pull request has been marked as ready for review. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR targets 2.3.x. It makes
self,parentandstaticinside closures bound withClosure::bind()(andClosure::call()/@param-closure-this) resolve against the bound class: in types (class constants,::class,new, static calls, static properties and, throughMutatingScope::getFunctionType(), closure parameter and return type hints) and in the rules that check them (ClassConstantRule, StaticMethodCallCheck, AccessStaticPropertiesCheck, InstantiationRule; the closure type-hint rules follow from the resolved type hints), at the top level and in functions as well as inside classes.The design is scope-based. The bind factory evaluates newThis/newScope at the call site and stores the bound classes in the scope's existing
inClosureBindScopeClasses; entering a class or function resets them, and the arguments of theClosure::bind()call itself keep resolving against the enclosing scope (regression test for phpstan/phpstan#6319).MutatingScope::resolveName(),resolveTypeByName()andgetFunctionType()honour the bound class outside a class too, and the rules ask a smallSelfClassResolverhelper which class self/parent/static are relative to.When newScope is not exactly one known class (one of several classes, or a
class-string/string/object of no known class), the scope stays bound to an unknown class (MutatingScope::UNKNOWN_CLOSURE_BIND_SCOPE_CLASS) instead of looking unbound or picking a candidate: self/static/parent resolve to the closest class all candidates extend (orobject/class-string/mixedwithout one), members are considered accessible when the bound class is unknown (with several known candidates, access granted by any of them counts, as before), and the rules stay silent;'static'andnullkeep meaning "no class". Unknown results surface asmixed/object/class-string, so at level 10 they are reported like any other unknown value.The first three commits are #6679's commits included verbatim (identical patches: named-argument handling in ClosureBindArgVisitor, the bind factory and ParametersAcceptorSelector) and drop out when this is rebased after #6679 lands. #6678 (typing
$thisfrom newThis) is not included; this PR does not depend on it to be correct, butself::calls to instance methods of a bound$thisare only as exact as the$thistype, which #6678 improves; likewise$this->memberaccessibility under an unknown scope class becomes exact only together with #6678. Every change to a turbo-shadowed class (MutatingScope, StaticCallHandler, ClassConstFetchHandler, NewHandler, ParametersAcceptorSelector, ClosureBindArgVisitor) carries its phpstan_turbo C++ twin in the same commit, with differential coverage (scope-family, walk-trace fixtures, parser-visitors).Known limitations:
static::inside a bound closure is the newScope class, notget_class($newThis). A closure passed to a@param-closure-thisparameter is assumed to be scoped to that class, soself::names it even outside a class (as withClosure::call()orbind($c, $o, $o), unlikebindTo($o)of an unscoped closure). A single bound class that does not exist (Unknown::class) is reported as not found; inside a class, constants and::classthen follow the enclosing class whilenew self()and the rules follow the unknown name.instanceof self/static/parentandcatch (self $e)inside a bound closure still resolve against the lexical class only (InstanceofHandler, ExistingClassInInstanceOfRule, CaughtExceptionExistenceRule); that is left for a follow-up. Constants through self/static in a scope bound to one of several classes are not typed as the union of the candidates' constants (no type rather than a wrong one).Disclosure: The first version resolved self/parent/static inside Closure::bind() closures through a parser-level annotation re-evaluated in the closure body's scope. An adversarial review by Claude Fable showed this disagreed with the scope the call site had already entered (object newScope not recognised, variable newScope picking up reassignments in the body) and that the annotation leaked into classes and functions declared inside the bound closure, producing false positives. The redesign drops the annotation and reads the bound class from the scope's existing inClosureBindScopeClasses, which the bind factory evaluates at the call site and which entering a class-like resets; MutatingScope::resolveName()/resolveTypeByName() now honour it outside a class as well, and the four rules consult it through a small SelfClassResolver helper. A second review led to distinguishing an ambiguous or unknown bound class from an unbound scope, and a third to keeping such a scope ambiguous everywhere (new/::class typing, type hints, accessibility, inside classes,
Closure::call()and@param-closure-thiswith an object of no known class). The full development history, including the earlier designs, is preserved at https://github.com/zonuexe/phpstan-src/commits/backup/bind-class-scope-redesign-history; the PR itself is a clean series with the same final tree.