Skip to content

Check all interfaces instead of only immediate ones for constructor enforcement in NewStaticRule#5740

Open
phpstan-bot wants to merge 2 commits into
phpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-3i7pgjh
Open

Check all interfaces instead of only immediate ones for constructor enforcement in NewStaticRule#5740
phpstan-bot wants to merge 2 commits into
phpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-3i7pgjh

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

When a class uses new static() and an interface enforces the constructor signature, PHPStan should consider the usage safe. However, when the interface was implemented by a parent class rather than the class itself, PHPStan incorrectly reported "Unsafe usage of new static()." because it only checked immediate interfaces.

Changes

  • Changed $classReflection->getImmediateInterfaces() to $classReflection->getInterfaces() in src/Rules/Classes/NewStaticRule.php (line 72) so the rule traverses the full interface hierarchy when looking for constructor-defining interfaces.

Root cause

NewStaticRule has two checks for interface-enforced constructors:

  1. Whether the constructor's prototype declaring class is an interface (line 68)
  2. Whether any interface of the class declares a constructor (line 72)

Check 1 fails when a concrete parent class also defines the constructor (the prototype points to the class, not the interface). Check 2 was the fallback, but it used getImmediateInterfaces() which only returns interfaces directly implemented by the current class — missing interfaces inherited from parent classes.

Analogous cases probed

  • NewStaticInAbstractClassStaticMethodRule: different concern (abstract class instantiation), does not check interfaces for constructor enforcement — not affected.
  • MethodPrototypeFinder::findPrototype(): uses getImmediateInterfaces() correctly because it does its own manual hierarchy walk via getParentClass().
  • OverridingConstantRule::findPrototype(): same as above — manual hierarchy walk, correct usage.
  • AllowedSubTypesRule: intentionally checks only direct parents for allowed subtypes — correct usage.

Test

Added tests/PHPStan/Rules/Classes/data/bug-10274.php with:

  • The exact reproducer from the issue: a class hierarchy with AbstractArray (concrete constructor) -> AbstractCollection -> IntermediateCollection implements ConstructorDefiningInterface -> SpecificCollection using new static().
  • A deeply-nested variant (DeeplyNestedCollection extends SpecificCollection) to verify the fix works at arbitrary depth.

Both cases expect no errors (empty expected-errors array).

Fixes phpstan/phpstan#10274

…nforcement in `NewStaticRule`

- Change `getImmediateInterfaces()` to `getInterfaces()` in
  `NewStaticRule::processNode()` so that constructor-defining interfaces
  inherited from parent classes are found when determining whether
  `new static()` is safe.
- Add regression test covering the reported hierarchy
  (class -> abstract -> abstract implementing interface -> concrete)
  and a deeply-nested variant.
Comment thread tests/PHPStan/Rules/Classes/data/bug-10274.php Outdated
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@VincentLanglet VincentLanglet self-assigned this May 23, 2026
@VincentLanglet VincentLanglet requested a review from staabm May 23, 2026 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants