From ae420d5fbb827fb1de212aa7d00568f7340415ae Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 20 Jan 2026 14:58:18 +0100 Subject: [PATCH 1/8] SplObjectStorage iterates over objects --- tests/PHPStan/Analyser/nsrt/bug-13985.php | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-13985.php diff --git a/tests/PHPStan/Analyser/nsrt/bug-13985.php b/tests/PHPStan/Analyser/nsrt/bug-13985.php new file mode 100644 index 0000000000..0224007d36 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13985.php @@ -0,0 +1,32 @@ + $value) { + assertType('int', $key); + assertType('object', $value); + } + } +} + +class X {} + +/** + * @param SplObjectStorage $splObjectStorage + * @return void + */ +function genericExample(SplObjectStorage $splObjectStorage): void +{ + foreach ($splObjectStorage as $key => $value) { + assertType('int', $key); + assertType('Bug13985\X', $value); + } + assertType('int', $splObjectStorage->getInfo()); + +} From 163f90741e0e8dcd2c8202eb8798d77a575e3971 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 20 Jan 2026 15:08:50 +0100 Subject: [PATCH 2/8] Revert "Cache ClassReflections" reverts https://github.com/phpstan/phpstan-src/commit/4a143bdda18c535d66ea5afbcea885c7cce63abd --- src/Type/ObjectType.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 3e755bab60..ca75971914 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -1746,10 +1746,10 @@ public function getClassReflection(): ?ClassReflection $classReflection = $reflectionProvider->getClass($this->className); if ($classReflection->isGeneric()) { - return $this->classReflection = $classReflection->withTypes(array_values($classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); + return $classReflection->withTypes(array_values($classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); } - return $this->classReflection = $classReflection; + return $classReflection; } public function getAncestorWithClassName(string $className): ?self From 50aa3b56db5f51f09e1edfef9fea4f9abf5c4d58 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 20 Jan 2026 15:16:46 +0100 Subject: [PATCH 3/8] Update ObjectType.php --- src/Type/ObjectType.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index ca75971914..3e2960a67c 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -1746,10 +1746,11 @@ public function getClassReflection(): ?ClassReflection $classReflection = $reflectionProvider->getClass($this->className); if ($classReflection->isGeneric()) { + // withTypes creates a new object, don't cache it in $this->classReflection return $classReflection->withTypes(array_values($classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); } - return $classReflection; + return $this->classReflection = $classReflection; } public function getAncestorWithClassName(string $className): ?self From 72901141d523d8b8316d530ece71b8be58a04e6a Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 20 Jan 2026 15:16:54 +0100 Subject: [PATCH 4/8] Update ObjectType.php --- src/Type/ObjectType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 3e2960a67c..0e7626426d 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -1746,7 +1746,7 @@ public function getClassReflection(): ?ClassReflection $classReflection = $reflectionProvider->getClass($this->className); if ($classReflection->isGeneric()) { - // withTypes creates a new object, don't cache it in $this->classReflection + // withTypes() creates a new object, don't cache it in $this->classReflection return $classReflection->withTypes(array_values($classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); } From 8eaad017b1925b8c21436a167d66ca57c51af260 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 20 Jan 2026 19:13:46 +0100 Subject: [PATCH 5/8] Update ObjectType.php --- src/Type/ObjectType.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 0e7626426d..b69fd0e5d1 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -1746,7 +1746,6 @@ public function getClassReflection(): ?ClassReflection $classReflection = $reflectionProvider->getClass($this->className); if ($classReflection->isGeneric()) { - // withTypes() creates a new object, don't cache it in $this->classReflection return $classReflection->withTypes(array_values($classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); } From 80c595e0d70a24fad1f0ce114c0c29b4f487df37 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 2 Feb 2026 17:00:26 +0100 Subject: [PATCH 6/8] Create bug-4789.php --- tests/PHPStan/Analyser/nsrt/bug-4789.php | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-4789.php diff --git a/tests/PHPStan/Analyser/nsrt/bug-4789.php b/tests/PHPStan/Analyser/nsrt/bug-4789.php new file mode 100644 index 0000000000..ae4aa20bb6 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-4789.php @@ -0,0 +1,11 @@ + Date: Mon, 2 Feb 2026 17:08:33 +0100 Subject: [PATCH 7/8] Update bug-4789.php --- tests/PHPStan/Analyser/nsrt/bug-4789.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Analyser/nsrt/bug-4789.php b/tests/PHPStan/Analyser/nsrt/bug-4789.php index ae4aa20bb6..4f47b1859d 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-4789.php +++ b/tests/PHPStan/Analyser/nsrt/bug-4789.php @@ -1,4 +1,4 @@ -= 8.0 namespace Bug4789; From bd8870c2926781ec33bbbc84e680b5c3cee5e11a Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Thu, 5 Feb 2026 10:09:13 +0100 Subject: [PATCH 8/8] Update ObjectType.php --- src/Type/ObjectType.php | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index b69fd0e5d1..85fcc4bf05 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -1735,21 +1735,20 @@ public function getNakedClassReflection(): ?ClassReflection public function getClassReflection(): ?ClassReflection { - if ($this->classReflection !== null) { - return $this->classReflection; - } + if ($this->classReflection === null) { + $reflectionProvider = ReflectionProviderStaticAccessor::getInstance(); + if (!$reflectionProvider->hasClass($this->className)) { + return null; + } - $reflectionProvider = ReflectionProviderStaticAccessor::getInstance(); - if (!$reflectionProvider->hasClass($this->className)) { - return null; + $this->classReflection = $reflectionProvider->getClass($this->className); } - $classReflection = $reflectionProvider->getClass($this->className); - if ($classReflection->isGeneric()) { - return $classReflection->withTypes(array_values($classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); + if ($this->classReflection->isGeneric()) { + return $this->classReflection->withTypes(array_values($this->classReflection->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes())); } - return $this->classReflection = $classReflection; + return $this->classReflection; } public function getAncestorWithClassName(string $className): ?self