Resolve self/parent/static in Closure::bind() scope#4081
Open
zonuexe wants to merge 3 commits into
Open
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
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.
Extract the bind-scope resolution into a shared ClosureBindScopeResolver service and use it both in ClassConstFetchHandler (type) and ClassConstantRule (rules), so self::/parent::/static:: class constants inside a bound closure no longer report "Using self outside of class scope" and resolve accessibility against the bound class.
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
… 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. |
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.
Resolves
self/parent/staticagainst theClosure::bind()scope (the 3rd argument) inside the bound closure body, for both type inference and the related rules.Rebased onto
2.2.xand reworked: the original inline patch inMutatingScope::getType()no longer exists after expression handling was split intoExprHandlerservices, so the logic now lives where each expression/rule resolves its class scope.What works now
Inside
Closure::bind(fn () => …, $newThis, Scope::class):self::CONST,parent::CONSTresolve against the bound scope.::class—self::class,parent::class.self::method(),parent::method()return types.self::$prop,parent::$proptypes.new self,new parent,new static.Using self outside of class scope.etc. and check accessibility against the bound class:ClassConstantRule,StaticMethodCallCheck,AccessStaticPropertiesCheck,InstantiationRule.This works even when
Closure::bind()is called outside a class context (the closure body type is inferred in the enclosing scope, where the closure-bind scope classes are otherwise unavailable — hence the class-name nodes are annotated byClosureBindArgVisitor).Approach
ClosureBindArgVisitorannotatesself/parent/staticclass-name nodes inside the closure body only (1st argument) with the bind scope argument. The$newThis/$newScopearguments are intentionally not rescoped — this keepsClosure::bind(self::cond() ? … : …, …)correct (see #6319).ClosureBindScopeResolver(new) resolves that annotation to aClassReflection, shared byClassConstFetchHandlerand the rules.MutatingScope::resolveName()/resolveTypeByName()honour the annotation so static calls / properties /newresolve without touching each handler.Issues
self::/parent::constant,::class, static method, and private-member cases now resolve. (Remaining:static::late static binding and the bound$thisobject type — a separate concern, overlaps Narrow down static and $this together #2972.)Not included (follow-ups)
Closure::bindTo()(Support Closure::bindTo() phpstan#8569) — separate call, separate visitor path.static::LSB + bound$thistype (Closure::bind does not rebind correctly phpstan#5987 remainder) — overlaps the stalled Narrow down static and $this together #2972; better decided after review of this approach.